Task Master
  • TypeScript 57.5%
  • Svelte 20.8%
  • Shell 11.6%
  • CSS 9.7%
  • Dockerfile 0.3%
Find a file
agra c2a5ae2680 fix: contain the task side pane and show relation status
Task detail columns size to minmax(0, 1fr) so a long description stays
in .task-main. Relation and subtask lists have no UA indent. Each
related task shows its status next to the title.
2026-08-17 12:00:35 +03:00
.forgejo/workflows Run the deploy job on the runner-mounted socket and the latest toolchain 2026-08-05 11:06:00 +03:00
deploy/runtime Declare gzip in the release closure 2026-08-05 11:10:18 +03:00
docs Match a task by its own id in search 2026-08-05 11:42:05 +03:00
scripts Clean the publish receipt on refused credentials 2026-08-05 10:21:14 +03:00
src lang: the sources speak the CUT surface 2026-08-15 10:29:19 +03:00
tests lang: the sources speak the CUT surface 2026-08-15 10:29:19 +03:00
web fix: contain the task side pane and show relation status 2026-08-17 12:00:35 +03:00
.dockerignore Define the deploy runtime and prove it before shipping 2026-08-04 20:12:31 +03:00
.gitignore Grow the JSON response buffer and log every refusal 2026-08-03 19:12:22 +03:00
docker-compose.yml Publish the sak image from the deploy action 2026-08-05 09:31:06 +03:00
Dockerfile Ship the rehearsed deploy image 2026-08-04 22:48:39 +03:00
README.md Match the documented build tag to the compose image 2026-08-05 09:56:15 +03:00

sak

sak is a task tracker for agents and the people working with them. One binary serves an HTTP API and the single-page app built on top of it, and keeps everything in one SQLite file: projects hold tasks; a task carries text, typed attributes, labels, comments and relations to other tasks. Every API request requires a bearer token minted by the same binary; the SPA assets are served without authentication, and the server listens on 127.0.0.1 unless --bind says otherwise.

Build from source

You need:

  • the sx compiler on PATH,
  • SX_STDLIB_PATH pointing at an sx standard library whose vendored SQLite is compiled with FTS5 — task search is an FTS5 index,
  • Node.js 22.13+ and npm, for the SPA.
export SX_STDLIB_PATH=/path/to/sx/library
cd web && npm ci && npm run build && cd ..
sx build src/main.sx -o sak

npm run build type-checks the SPA, runs its unit tests and writes web/dist/, which is what sak serve hands to the browser.

Run

./sak token create --db sak.db --name laptop
./sak serve --db sak.db

token create prints one token — sak_ followed by 64 hex characters — and that printed line is the only copy: the database stores its SHA-256. Mint one per client; deleting a row from the token table revokes it.

serve binds --bind (default 127.0.0.1) on --port (default 4345) and serves the SPA from --assets (default web/dist). --bind takes a numeric address — 0.0.0.0 and :: reach the whole network, a LAN address one interface — and anything else exits 2. --threads (default 16) sets how many requests are served at once, each on a SQLite connection of its own. The first line serve logs summarizes what it is running: version and build commit, bound address, database, assets, log settings and worker count. Open http://127.0.0.1:4345 and paste a token to get in. Reach the API with the same token:

curl -H "Authorization: Bearer sak_…" http://127.0.0.1:4345/api/health

Every request then leaves one line on stderr — the UTC instant, method, path, status, response bytes and duration, plus the cause on a 4xx or 5xx:

INFO:  2026-08-02T09:14:22Z GET /api/projects -> 200 412b 1ms
ERROR: 2026-08-02T09:14:21Z GET /api/tasks/8123 -> 404 21b 0ms get_task: NotFound

--log-level chooses how much of that is written: quiet keeps the refusals, requests (the default) adds a line per served request, debug adds the request size and the response content type to every line. The startup line is written at every level, and nothing is written to a file — redirect stderr to keep the lines. The last --log-ring lines (1000 by default, at most 100000) also stay in memory, where GET /api/logs serves them back newest first; docs/api.md documents that route.

Each command creates the database if it is missing.

Data

Everything lives in the one SQLite file named by --db. Migrations run when that file is opened, so upgrading is: stop the server, swap the binary, start it again. Back up by copying the file with the server stopped; restore by putting the copy back.

Run with Docker

Build the release archives on the host, then build and start the image:

bash scripts/release.sh
docker build -t git.swipelab.com/sx/sak:latest .
docker compose up -d

The compose recipe publishes port 4345, keeps the database in the sak-data volume, and restarts the container unless it is stopped. Create a token inside the container:

docker exec sak sak token create --db /data/sak.db --name laptop

Back up the database by stopping the service and copying the volume's sak.db:

docker compose stop
docker cp sak:/data/sak.db ./sak.db
docker compose start

Deploy

docs/deploy.md covers the manual deploy workflow that builds and publishes the sak docker image to the registry: how to trigger it, the runtime the job runs on, the secrets it reads, and how the NAS pulls and rolls back the published image. scripts/deploy.sh carries that logic and rehearses it locally with --local or --dry-run; deploy/runtime/manifest is the single authority on every external command the deploy path uses.

API

docs/api.md documents every route: projects, tasks, attributes, labels, comments, relations, search, saved views, paging and the revision protocol that guards concurrent writes.

Release tarballs

bash scripts/release.sh

builds the SPA and the server and writes three archives, each holding sak, web/dist/, this README and docs/:

  • dist/sak-<version>-macos-arm64.tar.gz
  • dist/sak-<version>-linux-x86_64.tar.gz
  • dist/sak-<version>-linux-arm64.tar.gz

The macOS archive is a native build, so the script itself runs on a macOS arm64 host; --linux-only drops it and packages the two Linux archives on any host. The Linux archives are cross-compiled with sx --target linux|linux-arm --self-contained, which needs zig on PATH (or in $SX_ZIG) for its libc. The script takes the compiler from $SX when that is set, and PATH otherwise.

The version is the VERSION constant in src/app.sx; it names the archives and answers GET /api/health. To cut a release, edit that constant and rerun the script. The release script supplies the short git sha of the checkout it packages. Direct builds carry a valid SAK_BUILD_COMMIT when supplied and answer unknown otherwise.

Unpack and run:

mkdir -p sak && tar -xzf sak-0.2.0-macos-arm64.tar.gz -C sak && cd sak
./sak token create --db sak.db --name laptop
./sak serve --db sak.db