Reference

The commands, the HTTP routes and the limits, for TaskFused 0.1. Every command except serve and version takes -db (the SQLite file, taskfused.db by default) and -namespace, and prints JSON; serve takes -db too. The contract on GitHub has the exact rules.

Names and States

An operation’s name is namespace, handler, version and key, each up to 256 bytes. The input is any bytes up to 1 MiB, and so is the result.

  • Same name, same input, with joining on: you get the existing operation, in whatever state it’s in, including a stored result or failure. No new attempt starts.
  • Same name, same input, joining off: an error saying it already exists.
  • Same name, different input, or a different retry policy: refused as a conflict.

States run queued, then running, then succeeded. A failed attempt goes to retry_wait and runs again while its attempts last, then failed. canceled can come from any unfinished state. succeeded, failed and canceled are final. Only an explicit retry reopens a failed operation; nothing reopens a succeeded or canceled one.

Commands

submit

taskfused submit -namespace N -handler H -version V -key K [-input TEXT] [-join] \
  [-max-attempts 3] [-retry-delay 1s]

Creates the operation, or joins it with -join. -max-attempts is 1 to 100 (default 3). -retry-delay is the first wait after a failure, 1ms to 1h (default 1s), and it doubles each time. The library accepts any bytes as input; the command takes text.

get, attempts, events

taskfused get      -namespace N -id ID
taskfused attempts -namespace N -id ID
taskfused events   -namespace N -id ID [-after 0] [-limit 50]

get returns the operation. attempts lists every attempt with its number, token, state, error and times. events pages through every change the operation went through, up to 200 at a time; pass the returned next_after to get the next page.

wait

taskfused wait -namespace N -id ID [-timeout 30s]

Returns when the operation is final. The result is base64. Giving up on a wait never cancels the work or anyone else’s wait. A successful exit means a final state was read, so check state to see whether the work itself succeeded.

cancel

taskfused cancel -namespace N -id ID

Marks unfinished work canceled and revokes its attempt, so no later result can be saved. A running handler sees it at its next lease renewal and should stop. Cancelling finished work changes nothing and says so (changed: false). Cancelling doesn’t undo outside effects that already happened.

retry

taskfused retry -namespace N -id ID -action-key KEY -expected-attempts COUNT \
  [-additional-attempts 1]

Reopens a failed operation with 1 to 100 more attempts, up to 1,000 over its life. -expected-attempts is the attempt_count you saw on the failed operation; if it has changed since, the retry is refused. Sending the same action key again returns the first answer without granting more, so it’s safe to repeat after a lost reply.

usage, set-limits, prune-results

taskfused usage         -namespace N
taskfused set-limits    -namespace N -max-operations 10000 -max-active 100
taskfused prune-results -namespace N -before 2026-10-01T00:00:00Z [-limit 50]

set-limits caps how many operations a namespace keeps and how many can be unfinished at once; zero means no limit, and new submissions over the cap are refused. Joining existing work still works at the cap. prune-results removes stored results of succeeded operations finished before the cutoff, up to 200 per call. The operation, its input and its history stay, marked result_pruned, and it will never run again. Pruning doesn’t shrink the file by itself.

serve, version

taskfused serve [-listen 127.0.0.1:8080]
taskfused version

serve runs the HTTP API and the status page on a loopback address only (127.0.0.1 or [::1]); port 0 picks a free one. Ctrl+C stops it cleanly. version prints the version, the commit it was built from, the Go version and the database schema.

HTTP API

All routes are under /api/namespaces/N. Request bodies are one JSON object sent as application/json, up to 2 MiB. Inputs and results are base64.

Method Path Does
POST /operations Submit; 201 when created, 200 when joined
GET /operations/ID The operation
GET /operations/ID/wait?timeout=10s Wait up to 30s for a final state
GET /operations/ID/attempts Every attempt
GET /operations/ID/events?after=0&limit=50 One page of the history
POST /operations/ID/cancel Cancel; body {}
POST /operations/ID/retry Retry a failed operation
GET /usage The namespace’s counts and limits

A submission:

curl -H 'Content-Type: application/json' \
  --data '{"handler":"echo","version":"v1","key":"example-1","input":"aGVsbG8=","join_existing":true}' \
  http://127.0.0.1:8080/api/namespaces/demo/operations

Optional submission fields are max_attempts and retry_delay, in nanoseconds. A retry body is {"action_key":"...","expected_attempts":1,"additional_attempts":1}. A failed or canceled operation comes back as a normal answer, 200 with its state.

Errors come back as JSON with these statuses: 400 bad request, 403 wrong host or a cross-site browser request, 404 not found, 409 conflict, 413 body too large, 415 not JSON, 429 namespace at its limit, 503 shutting down, 504 the wait or request ran out of time, 405 a method a route doesn’t take, and 500 for an internal storage error, with the details kept in the server’s log. A namespace is not a password: anything that can reach the loopback address can read every namespace.

Library

The Go package is github.com/taskfused/taskfused. Open opens or creates the file. Submit, Get, Wait, Cancel, Retry, Attempts and Events mirror the commands, and NewWorker with Run runs registered handlers with a set concurrency and lease. For your own worker loop there are Claim, Renew, Complete and Fail. Every change checks that the attempt still owns its lease. The README walks through each part.

Storage

One SQLite file per host, in WAL mode with synchronous = FULL, so each change is on disk before TaskFused reports it. SQLite keeps -wal and -shm files beside it while it’s open; back up with SQLite’s own tools, not by copying the main file alone. The file isn’t encrypted, so protect it with file permissions. Newer versions upgrade older files on open; stop the old processes first, because older binaries can’t open an upgraded file. The storage notes have the details.