Monorepo for Aesthetic.Computer aesthetic.computer

/run: ownership is the token, not the secrecy of the name master

`/run` took anyone's word for it. No credentials, `Access-Control-Allow-Origin: *`, straight through to a Redis publish — so the only thing between a stranger and every phone watching a channel was the channel's name, eight base64url characters kept unguessable on purpose. names.mjs says so outright: "a shared secret… make it unguessable." A secret cannot be printed on a rock. That is the whole reason the scanned address was a throwaway `~<channel>` rather than the piece's own URL: the piece's URL is public, and anything public was, under this scheme, a push credential. So ownership moved off the name and onto the token. A channel written `<handle>/<slug>` belongs to that handle and `/run` will only accept a push to it from that handle's access token, checked through the same `authorize` + `getHandleOrEmail` path store-kidlisp.mjs already uses. The name is then free to be public, which lets one string be the piece, the channel and the thing a camera points at: prompt.ac/@jeffrey/murafi Thirty-three bytes. One byte over what a version-2 code holds at correction level L, so the symbol is 29×29 rather than 25×25 — the cost of an address that outlives the session. Auto-publish is on by default now, because that address has to exist from the first frame or there is nothing to scan. `--no-autopublish` and `AESTHETIC_CODE_AUTOPUBLISH=0` opt out. Anonymous pushes answer 410, not 401: there is no way to retry the request as written. The VS Code extension was the last caller making them and is retired rather than grandfathered — an anonymous request cannot prove which tool sent it, so a door left open for the extension is a door left open for everyone. Signed out, there is no handle to own a channel and nothing published to point at, so `channel` and `scanUrl` fall back to the opaque route exactly as before. Every existing test covers that path and all 49 still pass unchanged; three new ones cover ownership, the fallback, and the token on the wire. Not done here, and it is what makes the address worth having: the piece at `@handle/slug` does not yet subscribe to the channel its own route names, so live pushes reach the `~<channel>` route and not the published one. Until that lands, a published piece updates when auto-publish republishes it — seconds, not keystrokes. The client side of that is disk.mjs, where `codeChannel` is read from the local store and a route carries no channel at all. Also worth knowing: the scanned address now changes when a piece is renamed, because the address is the piece. The old test asserting a stable QR across a rename still passes only because it exercises the signed-out path. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A1xZ1BonXHyds6EzF5H9Hb


+265 -102
5 changed files