diff --git a/README.md b/README.md
index 4170baa..b8234fc 100644
--- a/README.md
+++ b/README.md
@@ -55,7 +55,7 @@ takes the difficulty out of creating custom brute force jobs
- ✅ agent authentication
- ✅ client authentication
-# agent: accept and run jobs
+### agent: accept and run jobs
- ✅ securely connect to server
- ✅ retrieve assigned tasks
- handle smp correctly
@@ -67,7 +67,7 @@ takes the difficulty out of creating custom brute force jobs
- low priority: support finding _all_ matching inputs for a project, rather than just the first one
- the architecture currently doesn't stop on the first match so it could be a thing
-# client: submit jobs and view progress
+### client: submit jobs and view progress
- ✅securely connect to server
- command line interface
- `crossfire new`: create new crossfire project
@@ -79,3 +79,13 @@ takes the difficulty out of creating custom brute force jobs
- `crossfire cancel`: cancels submitted task
- `crossfire status`: check status of task (or network as a whole)
- low priority: gui interface (racket/gui & framework time)
+
+## misc
+
+### porting
+
+currently only linux is supported for the server and agent. the server might run on macOS but it's
+not guaranteed. if you are interested in porting this to a new platform, take a look at
+`agent-deployment` for the embedded build of racket for the all-in-one agent binary, and perhaps
+create additional makefiles for other platforms. additionally, platform-specific racket code is
+marked with an `XXX` comment
diff --git a/crossfire/client.rkt b/crossfire/client.rkt
new file mode 100644
index 0000000..ca94c62
--- /dev/null
+++ b/crossfire/client.rkt
@@ -0,0 +1,89 @@
+#lang racket/base
+;; crossfire: distributed brute force infrastructure
+;;
+;; Copyright (C) 2020 haskal
+;;
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU Affero General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU Affero General Public License for more details.
+;;
+;; You should have received a copy of the GNU Affero General Public License
+;; along with this program. If not, see .
+
+(require racket/function racket/list racket/match racket/vector
+ "comms.rkt" "info.rkt")
+
+(define *program* (format "~a-client" (#%info-lookup 'collection)))
+
+(define (cmd-new project-name)
+ (printf "creating new project ~a\n" project-name))
+
+(define (cmd-submit)
+ (displayln "submitting..."))
+
+(define (cmd-setup config-file)
+ (displayln "meow"))
+
+(module+ main
+ (require racket/cmdline)
+
+ (define (report-fatal-error cmd msg)
+ (printf "~a ~a: ~a\n" *program* cmd msg)
+ (exit 1))
+
+ (define (parse-new argv)
+ (command-line
+ #:program (format "~a new" *program*)
+ #:argv argv
+ #:args (project-name)
+ (cmd-new project-name)))
+
+ (define (parse-submit argv)
+ (command-line
+ #:program (format "~a submit" *program*)
+ #:argv argv
+ #:args ()
+ (cmd-submit)))
+
+ (define (parse-setup argv)
+ (command-line
+ #:program (format "~a setup" *program*)
+ #:argv argv
+ #:args (config-file)
+ (unless (file-exists? config-file)
+ (report-fatal-error "setup" "provided config file does not exist"))
+ (define config
+ (with-handlers ([exn:fail?
+ (lambda (ex) (report-fatal-error "setup" (exn-message ex)))])
+ (call-with-input-file config-file read)))
+ (cmd-setup config)))
+
+ (define *commands* (hash "setup" (list parse-setup "Set up server config")
+ "new" (list parse-new "Create a new project")
+ "submit" (list parse-submit "Submit a project for execution")))
+
+ (define (print-usage)
+ (displayln "crossfire-client [ ...]")
+ (displayln " where is one of")
+ (for ([(k v) (in-hash *commands*)])
+ (printf " ~a : ~a\n" k (second v))))
+
+ (match (current-command-line-arguments)
+ [(vector (? (curry hash-has-key? *commands*) cmd) argv ...)
+ ((first (hash-ref *commands* cmd)) argv)]
+ [(vector (or "-v" "--version"))
+ (printf "~a version ~a\n" *program* (#%info-lookup 'version))]
+ [(vector (or "-h" "--help") _ ...)
+ (print-usage)]
+ [(vector cmd _ ...)
+ (printf "~a: given invalid command ~s\n" *program* cmd)]
+ [(vector)
+ (printf "~a: expects 1 on the command line, given 0 arguments\n" *program*)])
+
+ (void))
diff --git a/crossfire/server.rkt b/crossfire/server.rkt
index 852962c..fd21097 100644
--- a/crossfire/server.rkt
+++ b/crossfire/server.rkt
@@ -34,6 +34,7 @@
(define *production?* #f)
+;; XXX : platform-specific
(define *config-root* (if *production?* "/etc/" "etc/"))
(define *state-root* (if *production?* "/var/lib/crossfire/" "lib/"))
(define *lib-root* (if *production?* "/usr/lib/" "lib/"))
@@ -76,9 +77,12 @@
(define current-revision (adapter-current-revision adapter))
(define target-revision (migration-revision (migration-most-recent base)))
(define plan (migration-plan base current-revision target-revision))
- (for ([migration (in-list plan)])
- (log-server-info "applying migration: ~a" (migration-revision migration))
- (adapter-apply! adapter (migration-revision migration) (migration-up migration)))
+ ;; TODO : automatically back up old db if there are migrations to apply
+ ;; or idk just ask the user to make backups tbh
+ (call-with-transaction db (lambda ()
+ (for ([migration (in-list plan)])
+ (log-server-info "applying migration: ~a" (migration-revision migration))
+ (adapter-apply! adapter (migration-revision migration) (migration-up migration)))))
(void))
(define-syntax-rule (define-stmt name what)