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>
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.