Files
platform/todo/headscale/device-invites-platform-side.md
pastilhasandClaude Fable 5 6071ded175 todo: one file per item, grouped by theme, with a generated index
TODO.md had reached 1050 lines and 72 open items across 28 sections, read mostly
on a phone. It is now a directory: todo/<theme>/<item>.md, one card per file, with
frontmatter carrying id, status, created date and order, and the prose body copied
across verbatim. TODO.md becomes a generated index (bun run todo:index).

Dates: where an item's prose stated one it is used; otherwise its old section's;
otherwise 2026-08-20, floored at the day before the split rather than invented.
date_source records which.

Shaped for a two-way Kanban board, filed as todo/tooling — a card is a file, id is
the identity, frontmatter is the machine's half and the body is the owner's.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 05:42:39 +00:00

1.5 KiB

id, title, theme, status, created, date_source, order
id title theme status created date_source order
headscale/device-invites-platform-side Device invites, platform side headscale done 2026-08-05 stated 5

Device invites, platform side (COMMS/OFFSCALE_INVITE_ENROLLMENT.md §5, built 2026-08-05). Authorize-new-device form, the one-time link with copy/share/QR, and the invite list with revoke — /_officer/enroll/invites in invites.ts, InvitesView. The records are not Officer's. They proxy to the server's Officer Companion, because the joining phone has to claim without an Officer account and this sidecar is loopback-only behind our auth; the spec's own "an invite must work when the platform is down" argument says the same. Officer stores no invite and no claim token. Live on all four servers since 2026-08-05. The 401 that blocked it was a companion-side prefix parse (a Headscale key prefix is a fixed 12 chars and may contain -; they split on the first one); fixed upstream. Verified end to end against pastilhas-eu: create returns the officer-offscale://join#… link, list shows the record, revoke flips it to revoked. The list envelope is not in the spec, so pickInvites takes the body's array whatever it is keyed under. Four gaps were sent back to the spec author: keys.ts has no sub-day key TTL for the 5-minute claim key, "the sidecar must refuse plaintext" is unenforceable behind nginx, tailnet is not a headscale concept and has to be recorded on the invite, and /api/v1/enroll/* collides with headscale's own namespace.