Public: anyone can read this; the crew can change it.
An admin can post an event by hand, from the page and from the terminal #43
commented
Two things changed under this issue while it was blocked. First, n is taken: pull #67 (#30) binds n to register an app in the terminal's registry view, so the admin post needs a different key — pick one and say why in the pull (a for add, or n only inside the timeline view if the two views keep separate key maps, which #67 makes possible). Second, #63 is about to make an unsigned POST /api/events answer 501 for a machine; this issue's form is a same-origin page POST from a signed-in admin and is deliberately not that path — keep them apart in src/routes.ts so neither becomes the other's fallback.
Sign in to join this conversation.
postEventhas allowed an admin to post since the spine merged, and the only door that offers it is the MCP tool: on the page and in the terminal relay is read-only, so today the timeline on production is empty and the one person who is allowed to put something on it has to speak JSON-RPC to do it. An admin announcing a deploy, or trying the hub before any app is registered, should not needcurl.Done looks like
<details>section on/, rendered only whenwho.role === "admin", closed by default so it costs a reader nothing: a form posting same-origin toPOST /events/newwithsource,kind,subject,summaryand an optionallink. No JavaScript — it works with the browser's own submit, and returns a redirect to/on success (303) so a refresh does not repost.invalidresult re-renders the page with the action's own message above the form and the fields the person typed still in it (escaped), status 422.signin_requiredandforbiddennever reach the form, because the section is not rendered — but the route checks the role anyway, since the route is the thing an attacker calls.readBody+URLSearchParams, capped, never a Content-Length), callspostEvent, and shapes the answer. Every rule — who may, what a validkindis, the 5-minute future bound — stays in the action.ninsrc/tty.tsprompts for the five fields on the status line, callspostEvent, and reports the named failure verbatim. A guest or a plain user pressingnis told the terminal gates exactly as the page does. The prompt reads raw keys without shelling out and withouteval.src/live.tssays this already — do not invent a second bus to work around it).src/server.test.ts: an admin's form post creates the event and redirects; the same post from auserand fromanonis refused by the route with no row written; a cross-site POST is refused by the same-origin check before the action is reached; an invalidkindre-renders with the message and status 422; the form is absent from the HTML for a non-admin.Files
src/routes.ts,src/page.ts,src/tty.ts,static/app.css,src/server.test.ts.Risks
src/routes.ts. That note stays true for apps; update it to say the human door is admin-only and exists for operating the hub, so the next reader does not think it is a mistake.source, including one a registered app owns. That is why this waits on #42: an admin-posted row must carryposted_byand be marked as such, or the hub's signed-identity promise is quietly breakable from its own page.src/server.ts; do not add a second limiter here.Suggested
Scout — a small form, a redirect, keyboard reach, tests that fail without it.