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
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.
| Method | Path | Purpose |
|---|---|---|
GET | /api/v2/manifest | The sha256 of every stored definition, so the package can compare and push only what differs. |
PUT | /api/v2/def | Store (create or replace) one queue or user definition. |
DELETE | /api/v2/def | Remove 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
| Method | GET |
|---|---|
| Path | /api/v2/manifest |
| Auth | Authorization: Bearer <master-token> |
| Parameters | None. |
| Body | None. |
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…"
}
| Field | Type | Meaning |
|---|---|---|
| (key) | string | A definition's server-relative path — <host>/taskhoster-queues/<name>.json for a queue, or <host>/taskhoster-users/<name>.json for a user. |
| (value) | string | The 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>
resize queue definition on task.example.com.| Parameter | In | Meaning |
|---|---|---|
path | query | The server-relative path, which must be <host>/taskhoster-queues/<name>.json or <host>/taskhoster-users/<name>.json. Any other shape is 400. |
| (body) | body | The 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 }
| Field | Type | Meaning |
|---|---|---|
ok | boolean | true — 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>
| Parameter | In | Meaning |
|---|---|---|
path | query | The 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 }
| Field | Type | Meaning |
|---|---|---|
deleted | boolean | true 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.
- Scan. Walk the content root for every
<host>/taskhoster-queues/*.jsonand<host>/taskhoster-users/*.json. These are the only files that sync; nothing else is considered. - Validate locally first. Parse and validate every definition — a queue as a queue,
a user as a user, whose
taskhoster-user-namemust match its filename. Nothing uploads unless all of them parse, so a broken edit never publishes a partial set. - Hash and read the manifest. Compute the sha256 of each local file and
GET /api/v2/manifestfor the server's hashes. - Push what differs.
PUT /api/v2/def?path=…for each file whose local hash differs from the server's (a new or changed definition). - 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.
| Status | When it appears |
|---|---|
400 | An 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. |
426 | The server speaks a different API version from the local package. Update the local package. |
500 | The server failed to write the definition file. |
404 | The wall: a missing or wrong master token, or an unknown route. Identical to a real not-found, so a probe learns nothing. |