authorize.ts had none, and its own header is what says why that mattered: every deny path in the platform ends there, and the failure mode of a permissive default is not a bug report, it is someone else's session. It had none for a structural reason rather than an oversight. Every path needed two database calls, and the only file here that mocks officerdb leaks its stub into whatever runs after it — so the obvious approach would have traded one untested file for a flaky suite. So the decisions are split from the fetching. resolveEffectivePermissions, decideApiRequest and decideWsProvider are pure functions of their arguments; the async wrappers keep the role cache, the fail-closed catches and the shape callers use. No behaviour change, and the live gate still answers 401 on file-browser, users and tasks. REAL registry keys throughout, deliberately: a suite built on invented permissions keeps passing after a rename, which is the failure an authorization table can least afford. If a key here disappears this file goes red. Pinned beyond the happy paths — the owner carries no grants to consult; core is write unconditionally; a confined grant is dropped without a Linux user and all three drop together; execution and admin grants in the table are ignored even though no CHECK stops the row existing; an unknown key is ignored without disturbing what is legitimately held; self-service is exact in method AND path; a socket needs no level; an unauthenticated caller is refused every door. Two gaps are stated in the file rather than left to be found: ownerOnly has no declaring permission yet, so testing it would test the fixtures; and the grant cache's contract is a claim about call sites, not logic. Found while writing them: readOnlyWrites paths are relative to the permission's api prefix, and getting that wrong fails as "no permission covers this path" rather than as a level error — a misleading message for a misleading path, now commented at the test that hit it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
76 lines
5.4 KiB
Markdown
76 lines
5.4 KiB
Markdown
# TODO — the index
|
|
|
|
Deferred work. **The items live in `todo/`, one file each**, grouped by theme; this file is the map
|
|
over them and is GENERATED — run `bun run todo:index` after any change rather than editing it here.
|
|
Format and the two-way-sync contract: `todo/README.md`.
|
|
|
|
**Context, corrected 2026-08-07.** This list used to open by saying Officer was "collapsing from
|
|
multi-tenant / open-source-ready to a **single-user platform**", and told you to treat multi-tenant
|
|
indirection as accidental complexity. **That direction was reversed.** The permission model shipped on
|
|
2026-08-07 to serve a real goal — deploy to the company server, onboard people, give each one their own
|
|
Gitea account through the platform. Per-user scoping is now a requirement, and the items that proposed
|
|
deleting it have been removed rather than left to mislead the next reader.
|
|
|
|
What did NOT reverse: `execution` permissions (terminal, chat, tasks, files, desktop, browser) run as
|
|
the owner's OS user and can never be granted. Indirection there really is accidental complexity.
|
|
|
|
## Urgent
|
|
|
|
- **due 2026-08-22** — **Settings to choose which server each feature uses** — [fleet/settings-to-choose-which-server-each-feature](todo/fleet/settings-to-choose-which-server-each-feature.md)
|
|
|
|
## Themes
|
|
|
|
**84 open, 25 closed**, across 29 themes.
|
|
|
|
| Theme | Open | Closed |
|
|
| --- | --- | --- |
|
|
| [Affordances](todo/affordances/) | 1 | 0 |
|
|
| [Speaking into a session from the backend](todo/agent-delivery/) | 2 | 0 |
|
|
| [Backburner](todo/backburner/) | 2 | 0 |
|
|
| [Background jobs](todo/background-jobs/) | 2 | 0 |
|
|
| [Chat interface](todo/chat/) | 1 | 0 |
|
|
| [Code editor](todo/code-editor/) | 1 | 0 |
|
|
| [Components — the shared UI layer](todo/components/) | 1 | 0 |
|
|
| [File watchers](todo/file-watchers/) | 1 | 0 |
|
|
| [Fleet — one command across every server](todo/fleet/) | 5 | 2 |
|
|
| [Headscale](todo/headscale/) | 0 | 5 |
|
|
| [Infra (alpha)](todo/infra/) | 6 | 0 |
|
|
| [Known bugs](todo/known-bugs/) | 3 | 2 |
|
|
| [Mobile apps](todo/mobile-apps/) | 3 | 0 |
|
|
| [Multi-user](todo/multi-user/) | 16 | 5 |
|
|
| [Notifications](todo/notifications/) | 1 | 0 |
|
|
| [Plugin ecosystem — before it opens to third parties (2026-08-18)](todo/plugin-ecosystem/) | 3 | 0 |
|
|
| [Plugin infrastructure](todo/plugin-infrastructure/) | 2 | 0 |
|
|
| [Plugin repos — what writing their READMEs turned up](todo/plugin-repos/) | 10 | 9 |
|
|
| [Plugin system — gaps the email extraction found (2026-08-18)](todo/plugin-system/) | 3 | 0 |
|
|
| [Prompt library](todo/prompt-library/) | 1 | 0 |
|
|
| [Settings — portable, and readable by an agent](todo/settings/) | 2 | 0 |
|
|
| [Shell — spotlight and the dock](todo/shell/) | 1 | 1 |
|
|
| [Sub-agents — agents spawning agents](todo/sub-agents/) | 1 | 1 |
|
|
| [Sudo terminal modal](todo/sudo-terminal-modal/) | 1 | 0 |
|
|
| [Tooling — the list about the list](todo/tooling/) | 1 | 0 |
|
|
| [Transmission](todo/transmission/) | 6 | 0 |
|
|
| [URL state — routing, query strings and selection](todo/url-state/) | 4 | 0 |
|
|
| [Waiting on the owner to run it](todo/waiting-on-owner/) | 2 | 0 |
|
|
| [Workspaces & Panels](todo/workspaces-panels/) | 2 | 0 |
|
|
|
|
## Records — no items, kept for the reasoning
|
|
|
|
- [Affordances (2026-08-19)](todo/affordances-runtime/_index.md)
|
|
- [Email](todo/email/_index.md)
|
|
- [Inputs outside forms (2026-08-19) — swept, fixed, closed](todo/inputs-outside-forms/_index.md)
|
|
- [Release-binary dependencies (2026-08-19)](todo/release-binaries/_index.md)
|
|
|
|
## Newest ten
|
|
|
|
- `2026-08-22` **A plugin's permission should be on for every role by default, off only on request** — [multi-user/a-plugin-s-permission-should-be-on-for-every](todo/multi-user/a-plugin-s-permission-should-be-on-for-every.md)
|
|
- `2026-08-22` **A user's settings should export and import as a plain file** — [settings/a-user-s-settings-should-export-and-import-as](todo/settings/a-user-s-settings-should-export-and-import-as.md)
|
|
- `2026-08-22` **The same for system settings, owned by the Super Admin** — [settings/the-same-for-system-settings-owned-by-the](todo/settings/the-same-for-system-settings-owned-by-the.md)
|
|
- `2026-08-21` **No way to see which affordances are reaching your agents, or to turn one off** — [affordances/no-way-to-see-which-affordances-are-reaching](todo/affordances/no-way-to-see-which-affordances-are-reaching.md)
|
|
- `2026-08-21` **Generalise it: deliver a turn to a named session, from anywhere in the backend** — [agent-delivery/generalise-it-deliver-a-turn-to-a-named](todo/agent-delivery/generalise-it-deliver-a-turn-to-a-named.md)
|
|
- `2026-08-21` **The cost of resuming grows with the transcript** — [agent-delivery/the-cost-of-resuming-grows-with-the-transcript](todo/agent-delivery/the-cost-of-resuming-grows-with-the-transcript.md)
|
|
- `2026-08-21` **'An agent spawned by a background job is invisible in the interface'** — [background-jobs/a-spawned-agent-is-invisible-in-the-interface](todo/background-jobs/a-spawned-agent-is-invisible-in-the-interface.md)
|
|
- `2026-08-21` **Show how long a turn took, beside its cost and tokens** — [chat/show-how-long-a-turn-took-beside-its-cost](todo/chat/show-how-long-a-turn-took-beside-its-cost.md)
|
|
- `2026-08-21` **Map every custom component, centralise it, and let a theme override it** — [components/map-every-custom-component-and-centralise-it](todo/components/map-every-custom-component-and-centralise-it.md)
|
|
- `2026-08-21` **Watch a directory, and attach actions to what lands in it** — [file-watchers/watch-a-directory-and-attach-actions-to-what](todo/file-watchers/watch-a-directory-and-attach-actions-to-what.md)
|