Expand description
The per-server homepage and operations dashboard: this server, reporting
on itself. docs/web-surfaces.md calls this surface 3 of the project’s
four websites, and draws the boundary this module keeps: it is served by
didbot-serve, about the deployment serving it, one per deployment
rather than one for the whole project, and it owns no policy — policy is
written from a separate, statically-hosted origin (surface 2) and only
ever read here, never edited.
It answers before anyone signs in: about says what this deployment
is (its zone) and what it holds (bot.did.stats’s own numbers, reused rather than
reread). Everything past that is the authenticated operations view.
plan/ops-dashboard.md sets one rule above everything else it asks for:
never display invented data as if it were real. Every panel this module
serves answers in exactly one of three states, carried in
crate::surface::PanelState rather than left for a client to infer
from shape:
- Live — a real capability, read fresh from this process’s own state.
- Not implemented — the epic it needs does not exist yet
(
write-policy,scope-policy,app-allowlist,oauth). Names what is missing and carries no numbers at all, because a plausible-looking zero is a lie this dashboard is built specifically not to tell. - Unreachable — a real capability whose read failed, which is a different fact from a real capability that read as zero.
panel_state_never_carries_numbers_without_a_backing_capability is the
mechanical guard on that rule: it asserts, at the JSON level, that
PanelState::NotImplemented and PanelState::Unreachable can never
serialize a data field, so a future panel cannot regress into showing a
number for a capability that is not there.
§What is public here, and what is not
Public: the static shell and its assets, about and capabilities,
and the client metadata document — about is bot.did.stats’s own
public numbers, capabilities names which epics are unbuilt, which
plan/ already says out loud, and the metadata document’s own URL is
the client_id, so the operator’s authorization server has to be able
to fetch it before it will show them a consent screen.
Behind the operator sign-in: the e-stop’s status and its two controls,
the bot.did.operator records the poll last read out of the operator’s
own repository, and the relay announce. What authenticates them is
crate::operator — atproto OAuth against the operator’s own server,
requesting atproto and nothing else, with the verified sub compared
against this deployment’s --operator. No operator credential reaches this
server, which is what plan/handshake.md is protecting: the sign-in
proves control of a DID, and this server then issues a session of its
own.
An anonymous, internet-reachable halt would be a denial of service rather than an emergency stop, which is why throwing costs a sign-in too and not only releasing.
The session list still has no data behind it, and renders
PanelState::NotImplemented naming the epic it waits on. That is the
same honesty rule the rest of this module follows: a panel whose data
this server does not have is a panel that says so, not one showing a
plausible zero.
The per-app panels — live logins, refusals and writes — are
crate::dashboard_apps’s, and the account list and detail are
crate::dashboard_accounts’s, both merged into this module’s router.
Structs§
- About
View 🔒 - What the unauthenticated homepage panel reports.
- Acted 🔒
- What one account route did, as it answers.
- Announce
Body - What
/dashboard/api/relay/announceaccepts. - Callback
Query - What the authorization server sends back.
- Delete
Body - What
/dashboard/api/accounts/{did}/deleteaccepts. - Login
Query - What
/dashboard/loginaccepts. - Operator
Record 🔒View - One
bot.did.operatorrecord in the operator’s own repository, as the panel shows it. - Throw
Body - What
/dashboard/api/estop/throwaccepts.
Constants§
- DASHBOARD_
CSS 🔒 - DASHBOARD_
JS 🔒 - DASHBOARD_
WASM 🔒 - The dashboard’s wave field, as compiled in. Empty when the opt-in
wasm build has not been run; see
build.rs. - INDEX_
HTML 🔒
Functions§
- about 🔒
GET /dashboard/api/about- account_
at 🔒 - The account the path segment names, or the refusal to answer with.
- account_
credentials 🔒 POST /dashboard/api/accounts/{did}/credentials- account_
delete 🔒 POST /dashboard/api/accounts/{did}/delete- account_
erase 🔒 POST /dashboard/api/accounts/{did}/erase- account_
lift_ 🔒quarantine POST /dashboard/api/accounts/{did}/lift-quarantine- account_
lock 🔒 POST /dashboard/api/accounts/{did}/lock- account_
revoke_ 🔒tokens POST /dashboard/api/accounts/{did}/revoke-tokens- account_
unlock 🔒 POST /dashboard/api/accounts/{did}/unlock- acted 🔒
Actedfordid, reading the entry the action just wrote.- as_
the_ 🔒operator - Moves the latch inside a span naming this deployment’s operator, so the line the latch logs for the throw or the release says who asked. The span carries the DID and nothing from the session.
- callback 🔒
GET /dashboard/callback- capabilities 🔒
GET /dashboard/api/capabilities- client_
metadata 🔒 GET /dashboard/oauth/client-metadata.json- css 🔒
GET /dashboard/dashboard.css- did_it 🔒
- The envelope every account route answers with: what was asked for, and each account it reached.
- estop_
release 🔒 POST /dashboard/api/estop/release- estop_
status 🔒 GET /dashboard/api/estop- estop_
throw 🔒 POST /dashboard/api/estop/throw- index 🔒
GET /dashboard- js 🔒
GET /dashboard/dashboard.js- login 🔒
GET /dashboard/login- logout 🔒
POST /dashboard/logout- no_
operator 🔒 - The page
loginandcallbackanswer on a deployment started with no--operator. - operator_
records 🔒 GET /dashboard/api/operator-records- refused 🔒
- A registry refusal, as the operator reads it.
- refused_
partway 🔒 - A registry refusal that came after the action had already reached some accounts, with those accounts beside it: a caller that saw only the refusal would not know what had already happened.
- relay_
announce 🔒 POST /dashboard/api/relay/announce- routes
- Builds the dashboard’s routes, to be merged into the main router.
- subtree_
of 🔒 didand everything beneath it, deepest first — the order a hard delete has to take them in.- wasm_
asset 🔒 GET /dashboard/wasm/{file}