How humans see and steer the graph — safely. Auth, audit, and Cypher-backed views. ~11 min.
Builds on: L14 · L12New: RBAC + auth0New: audit logging
In L14 we named the admin-panel as the real human-facing read
surface but didn't open it. It's the operator console — a multi-feature internal web UI over
everything you've built: explore the graph, investigate an address, manage focus tokens and IAM, edit rules. Because
it gives privileged humans direct access to a risk system, its defining features aren't the views — they're the
access control and the audit trail wrapped around them.
What it actually contains (orientation)
A console of features (each a server-rendered, Cypher-backed view): a graph explorer / viz
(graph_viz_edges.go), an investigator (investigator.go),
node lookup (lookup.go), IAM / roles
(iam_resolver.go), focus-token admin, rule editing, plus team/CRM tooling.
Don't memorise the list — internalise the shape: many small features sharing one auth + audit + render spine.
1 · Every request runs an auth gauntlet (RBAC)
This is the heart of it. The panel uses auth0 for identity (who are you?) and a
permission guard for authorization (are you allowed this?). Each route declares the
permission it needs (cmd/admin-panel/auth.go):
// permGate wraps every handler; requiredPermission(method, path) → the needed permfuncpermGate(g *authz.Guard) func(http.Handler) http.Handler { /* 403 if missing */ }
🔗 L14's "auth lives on the read side", made concrete
Recall the write path (L4·L9) never faces a user — but the panel does, so it's where authn/authz live. Note the
read/write permission split (focus_tokens:read vs :write): viewing data and
changing it are different grants. Least-privilege by construction.
2 · Every privileged action is audited
Identity + permission isn't enough for a tool that can change a live risk system — you also need to know
who did what. The panel writes an audit log of operator actions to a per-operator Redis
stream (cmd/admin-panel/auth0/xpapi_audit.go):
// xpapi:audit:{operatorEmail} — a Redis stream, one per operatorfunc (a *RedisAuditor) Record(ctx, operatorEmail string, evt AuditEvent) // append on each actionfunc (a *RedisAuditor) Recent(ctx, operatorEmail string, limit int) ([]AuditEvent, error) // read back for display
Privileged routes (the audited "xpapi" — extended API — surface, xpapi_routes.go) record an
AuditEvent per action, and Recent surfaces a history in the UI. Auth says you can;
audit records that you did.
Why this rigor — your instinct is right
This is a tool that can mint focus tokens into the monitored set, edit IAM, change rules — actions that move what
the whole system watches and alerts on. That's exactly the surface you'd want gated and logged. The same
"privileged action = needs an audit trail" instinct you'd apply to a treasury multisig or a protocol admin panel.
Notice the auditor even reuses a Redis stream — the same primitive as blocks (L7) and alerts (L15). One toolbox, everywhere.
3 · The views: server-rendered, Cypher-backed reads
Behind the auth gauntlet, a feature is a controller that runs an anchored Cypher read and renders
HTML. It obeys the L2 rules you know: graph_id-scoped, anchored at a known id, bounded — never a full
scan (this is a 1.8M-node graph, L14). The graph-viz feature is the most graph-shaped: it pulls a node's
neighbourhood as edges to draw, deliberately excluding super-node and analytical edges so the picture stays legible.
A nice discipline: features are self-documenting
Per the repo's CLAUDE.md, every Cypher-backed admin-panel feature ships a doc
(cmd/admin-panel/docs/<section>/<slug>.md) with a mermaid diagram, a "renders-on" table, the actual
Cypher, and an external-deps table — and tests reject a feature whose doc has dangling links or
an orphaned data-doc-slug. The UI and its documentation are kept in lockstep by CI. (Same
"enforce the invariant mechanically" spirit as L9's forbidigo guard.)
4 · Where it sits in the whole system
The admin-panel is a pure consumer: it reads the graph (and its pre-computed risk fields, L13) and
lets operators perform bounded privileged writes — which, like all bulk writes, still flow through the
graph-writer (L9); the panel doesn't bypass the single-writer discipline.
It adds no new data-plane mechanism — it's the human control plane over the machine you've spent sixteen
lessons understanding.
The recurring toolbox, one last time
Look what the panel is built from: Redis streams (audit, L7/L15), anchored Cypher (L2),
pre-computed fields (L13), the graph-writer for writes (L9), CI-enforced
invariants (L9). Nothing new — the same patterns, recombined into a human interface. That's the sign
you now understand the system: every new corner is assembled from parts you already know.
Check yourself
1. The admin-panel is, in one phrase…
2. auth0 vs the permission guard — what's the division of labour?
3. Why are focus_tokens:read and focus_tokens:write separate permissions?
4. What does the per-operator xpapi:audit:{email} Redis stream provide?
5. A controller rendering a node's neighbourhood must follow which rule from L2?
6. When an operator changes focus tokens via the panel, the actual graph write…
7. The CI rule that rejects an admin-panel feature whose doc has dangling links is the same spirit as…
8. The deeper takeaway from the admin-panel is…
↳ Ask your teacher
Try: "Show me requiredPermission's route table," ·
"How does the graph-viz controller build its Cypher?" ·
"What's in an AuditEvent?" ·
"How does auth0 map a user to permissions (the Guard)?"
What you can now do
Describe the admin-panel as the operator console — the human control plane, not a data-plane binary.
Explain audit logging of privileged actions and why a tool with this power needs it.
Recognise its views as anchored, Cypher-backed, L2-compliant reads, with writes still going through the graph-writer.
See that it's built entirely from the system's existing toolbox — the mark that your model is complete.
🏁 Effectively the whole codebase
Data plane (L1–L11) · analytics (L6·L13·L16) · product/consumer side (L12·L14·L15) · human control plane (L17).
You've now opened every major corner of risk-graph-indexer — and crucially, you can tell when a "new" part is just
old patterns recombined. That's mastery of a system, not just familiarity.