Client-Server Synchronisation

Taskhoster mirrors your local package up to the server over a private, token-gated API on the server's IP. The local package pushes only the queue and user definitions; the tasks themselves are never synced — they are posted by producers and claimed by workers, and live only on the server. Every route lives under /api/v2, the version the local package pins, and carries Authorization: Bearer <master-token>. The read-only endpoints behind the log browsers share this shape on the Tools page.

GET    /api/v2/manifest
PUT    /api/v2/def
DELETE /api/v2/def
The sync API: read the manifest, then store or remove one definition.

Private API

Every call carries the master token as Authorization: Bearer <master-token>. There is nothing else to the surface: no dashboard, login, session or cookie. With a valid token the HTTP status line carries the outcome: 200 ok, 400 a bad request (an invalid path or definition), 426 a request on a different API version's path, and 500 a failed write. Without a valid token — a missing or wrong master token, or an unknown route — every reply is an identical 404 wall, so a probe cannot tell the API is there at all (see Server). The routes are versioned under /api/v2/; the local package pins the prefix to its own version, so a server on a different version answers 426 (fail fast) rather than the package silently following it onto a contract it does not speak.

MethodPathPurpose
GET/api/v2/manifestThe sha256 of every stored definition, so the package can compare and push only what differs.
PUT/api/v2/defStore (create or replace) one queue or user definition.
DELETE/api/v2/defRemove one definition the server holds but the package no longer has.

GET /api/v2/manifest

Returns the sha256 of every definition the server stores, keyed by its server-relative path, so the local package can compare hashes and upload only the files that differ. It reads the stored file bytes, so a definition the package pushed and one it is about to push hash identically when their contents match.

Request

MethodGET
Path/api/v2/manifest
AuthAuthorization: Bearer <master-token>
ParametersNone.
BodyNone.

Response

A 200 JSON object mapping each stored definition's path to its sha256 hex:

{
  "task.example.com/taskhoster-queues/resize.json": "9c1f…",
  "task.example.com/taskhoster-users/alice.json":   "3b7a…"
}
The sha256 of every stored queue and user definition, keyed by path.
FieldTypeMeaning
(key)stringA definition's server-relative path — <host>/taskhoster-queues/<name>.json for a queue, or <host>/taskhoster-users/<name>.json for a user.
(value)stringThe sha256 hex of that file's bytes. Equal hashes ⇒ the local and stored definitions are identical, so nothing is pushed.

PUT /api/v2/def

Stores one definition, creating or replacing it. The path query names which definition; it is validated to be one of the two definition-folder shapes, and the server re-validates the body by folder: a taskhoster-queues file is parsed as a queue definition, a taskhoster-users file as a user definition (and the user's taskhoster-user-name must equal the filename stem). Only a definition that parses is written; a bad path or an invalid body is rejected before anything touches disc.

Request

PUT /api/v2/def?path=task.example.com/taskhoster-queues/resize.json HTTP/1.1
Authorization: Bearer <master-token>
Content-Type: application/json

<the raw JSON definition>
Store the resize queue definition on task.example.com.
ParameterInMeaning
pathqueryThe server-relative path, which must be <host>/taskhoster-queues/<name>.json or <host>/taskhoster-users/<name>.json. Any other shape is 400.
(body)bodyThe raw JSON definition. It is parsed and validated by folder — a queue file as a queue, a user file as a user (name must match the filename).

Method PUT, plus Authorization: Bearer <master-token>.

Response

A 200 JSON object on success:

{ "ok": true }
The definition was validated and written.
FieldTypeMeaning
okbooleantrue — the definition parsed, validated and was stored.

A bad path or an invalid definition is 400 with an { "error": … } body naming the file and the problem; a failed write is 500 — see Errors.

DELETE /api/v2/def

Removes one definition that the server holds but the local package no longer has. Deleting a definition that is already absent is still a 200 — the desired state is reached either way, so delete is idempotent.

Request

DELETE /api/v2/def?path=task.example.com/taskhoster-queues/old.json HTTP/1.1
Authorization: Bearer <master-token>
Remove a queue definition that is gone locally.
ParameterInMeaning
pathqueryThe server-relative path to remove, one of the two definition-folder shapes. Any other shape is 400.

Method DELETE, plus Authorization: Bearer <master-token>; no body.

Response

A 200 JSON object:

{ "deleted": true }
Whether a file was actually removed; either way the state is reached.
FieldTypeMeaning
deletedbooleantrue if a definition was removed; false if the path was already absent (still a 200).

A bad path is 400 with an { "error": … } body — see Errors.

Synchronisation Process

A run mirrors only the definitions, and only the differences. It validates everything locally before it touches the server, hashes each file, reads the server's manifest, and then pushes and prunes so the two sides agree. It is stateless: every hash is recomputed each run.

  1. Scan. Walk the content root for every <host>/taskhoster-queues/*.json and <host>/taskhoster-users/*.json. These are the only files that sync; nothing else is considered.
  2. Validate locally first. Parse and validate every definition — a queue as a queue, a user as a user, whose taskhoster-user-name must match its filename. Nothing uploads unless all of them parse, so a broken edit never publishes a partial set.
  3. Hash and read the manifest. Compute the sha256 of each local file and GET /api/v2/manifest for the server's hashes.
  4. Push what differs. PUT /api/v2/def?path=… for each file whose local hash differs from the server's (a new or changed definition).
  5. Prune what's gone. DELETE /api/v2/def?path=… for each path the server has that is no longer present locally.

Every call uses the /api/v2 prefix from the package's own version, so a server on a different version answers 426 and the run fails fast with a clear message, rather than a silent misparse of a contract it does not speak.

Errors

With a valid token, a failure is an HTTP status with a short { "error": … } body. Without one, every reply is the same 404 wall.

StatusWhen it appears
400An invalid path (not one of the two definition-folder shapes), or a definition that fails to parse or validate. The message names the file and the problem.
426The server speaks a different API version from the local package. Update the local package.
500The server failed to write the definition file.
404The wall: a missing or wrong master token, or an unknown route. Identical to a real not-found, so a probe learns nothing.