the notify sidecar stops trusting whoever reaches it

to send one message to four accounts an hour ago i found the sidecar's
ephemeral port and POSTed to it directly, once per user, with no
authentication of any kind. that worked, which was the problem: the same
door was open to anything else on the machine, including a commander with
a shell.

two changes.

POST /api/notify/broadcast, owner only via `ownerOnly` on the permission —
the supported way to say one thing to everyone. teams will narrow it later;
today the choice is everyone or one person.

and the sidecar now requires a secret on everything under /_officer/. it
is generated per boot, held in memory, announced to the platform over the
registration socket beside the port, and never written to disk.

X-Officer-User could not carry this on its own, and my first attempt got
that wrong: it says WHO is asking, and a caller on loopback can write it
itself. only the secret says the PLATFORM is asking. so the check covers
the whole /_officer/ surface rather than just the send route — /_officer/
devices takes the same header to decide whose phones are listed and
deleted.

what it bounds, honestly: a MEMBER's commander runs as its own Linux user
and cannot read this process, so for every account but the owner's this is
kernel-enforced. the owner's own commander runs as the owner, same as the
platform, and is not bounded by this and cannot be — the same reason
`execution` permissions are never grantable. the owner's decision, and
this is a real improvement rather than a boundary.

`bun run notify:test` posts to that port and will now be refused. it is a
developer CLI on a box where the platform is running; left to fail loudly
rather than given a way in.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
This commit is contained in:
2026-08-24 16:29:48 +00:00
co-authored by Claude Opus 5
parent de633f8697
commit cb2dcfa4cc
7 changed files with 151 additions and 10 deletions
+13 -1
View File
@@ -128,7 +128,19 @@ export type SidecarCommand =
* `<name>:server`, where the name is the sidecar's own — `music:server`, `pty:server`, and whatever a
* plugin nobody has written yet decides to call itself. Deliberately open: see the note in SidecarEvent.
*/
export type SidecarPortAnnouncement = { type: `${string}:server`; port: number };
export type SidecarPortAnnouncement = {
type: `${string}:server`;
port: number;
/**
* A per-boot secret for callers INSIDE the platform that reach this sidecar directly rather than
* through its proxy — announced here, in memory on both sides, never written to disk.
*
* Optional because only a sidecar with a producer path to guard has one. `notify` is the first: its
* send route accepts a target user in the body, which loopback alone stopped being enough to protect
* once commanders could run a shell on the same machine.
*/
secret?: string;
};
export type SidecarEvent =
| { type: 'pong'; id: string }