a job is: - id - executable data - marking: new/taken/done/failed - dates: created, taken, finished - metadata: dict[str,str] keys cannot include : or ' ', neither can include \n server protocol: case-insensitive commands separated by \n on error, send back '?', else send back 'OKAY' server verbs: - '' ignored - NEW JOB len server reads in len bytes, and sends back a job id - LIST marking? JOBS server sends back a list of all jobs, in the format: id\tcreated\tlen\tmarking - INFO [FOR JOB id] IS server sends back a response of the form: SCRIPT IS len BYTES MARKED AS marking CREATED|TAKEN|FINISHED|UPDATED AT iso-8601 - SCRIPT [FOR JOB id] IS len? get the script as len\ndata, or update the script. if a job is not marked 'new', the update fails - META [FOR JOB id] IS get metadata for id as key: value lines, terminated with a blank line - META key [FOR JOB id] IS value? set/get id.key - TAKE JOB id? mark a job as taken, record timestamp if n is unset, pick the next available job and send it back - MARK [JOB n] AS marking mark a job as complete, record timestamp - CANCEL [JOB n] delete a job from the job queue - all others: send back ?