Interfaces & ops
Dashboard & admin screen
A static SPA-style front end over the .NET 10 minimal-API backend. Five pages share one nav and stylesheet — Dashboard, Setup Guide, Graph, Quality, and Admin. The dashboard is a public, always-live readout; the admin screen is a full operational control room whose every action is gated to loopback or the tailnet. This page documents every dashboard tile and every admin card.
The trust boundary
The admin page is public HTML, but every action it takes hits /api/admin/*, which is
gated to loopback OR the Tailscale tailnet only — IsLoopback(ip) || IsTailnetAddress(ip)
(Program.cs:3049-3064), matching IPv4 CGNAT 100.64.0.0/10 and IPv6 ULA
fd7a:115c:a1e0::/48. Off-tailnet requests get HTTP 403 (:3083-3085), and
admin.html swaps its whole panel for a gate card carrying an SSH-tunnel command. The public nginx vhost also
blocks /api/admin/ outright. Dashboard, search, and /mcp stay public. Dashboard tiles
are driven by script.js fetching /api/stats, /api/distribution, and
/api/recent every 15s (script.js:11-19) plus /api/caller-recall
best-effort.
Anyone can watch the brain's health, but only someone on the private network (or an SSH tunnel) can change a setting or trigger a run — a leaked admin URL is inert from the public internet.
Prometheus /metrics — the same gate, a different consumer
PR #196 (1e64463) exposes Prometheus-format metrics at GET /metrics via
OpenTelemetry.Exporter.Prometheus.AspNetCore 1.16.0-beta.1, mapped
after the same gate middleware as /api/* and /hook/* — loopback or
tailnet, or a bearer token — and never added to Security:PublicPaths, so it is never public.
Metrics:Enabled (default true) is the single kill switch: off skips both the exporter
registration and the route mapping. Verified live after deploy: /metrics returns 401 from the
public hostname and serves on loopback.
Instruments: process working set and GC heap size, an MCP tool-call counter plus a latency histogram, gate
allow/deny counters, ingest running / last-duration gauges, Groq lifetime cost, overlay roots registered, and
precise (Roslyn) workspaces loaded. Sample live values captured after deploy:
ledger_overlay_roots_registered 55, ledger_groq_lifetime_cost_usd 23.11679605000003,
process_working_set_bytes 4375715840, and — after one real call —
ledger_tool_calls_total{tool="symbol_lookup"} 1.
observability/
(docker compose up -d) — both bound to 127.0.0.1 via
network_mode: host, data persisted under /opt/arch-ledger-observability/, ports 9090
(Prometheus) and 3000 (Grafana), no nginx change. It is not deployed or proxied anywhere; it is a local
operator convenience for graphing what /metrics already exposes, documented with a one-command
start in observability/README.md.The dashboard tiles are a snapshot fetched every 15s; a scrape endpoint on the standard Prometheus contract lets an operator graph trends — RSS sawtooth, gate denials/hour, query latency percentiles — over time, with a real-time-series store, using the exact same trust boundary the rest of the admin surface already relies on.
Dashboard tiles (index.html) — "Architectural Memory Hub"
Fifteen live tiles, each refreshing every 15s. Each carries its own concrete benefit:
| Tile | What it shows | Why it matters |
|---|---|---|
| Total Memory Chunks | Headline = live chunks (current is_latest), subtext =
distinct repos + total points incl. superseded — /api/stats
liveChunks/totalChunks/distinctRepos (script.js:69-76;
Program.cs:371). | The live-vs-total split makes version bloat visible at a glance. |
| Distinct Patterns | Distinct patterns with a "layers covered" subtext
(script.js:77-78). | Signals the brain indexes real architecture, not just files. |
| Last Sync | Relative time of last ingest + files processed/scanned · chunks · duration, with
error and budget-skip annotations (script.js:83-90,110-113). |
Freshness and health; surfaces budget-throttled runs. |
| MCP Activity | Lifetime query_ledger count, sync count, last-activity relative time. | Proves agents are actually using the brain. |
| Groq Spend | Lifetime $ + calls + last-run cost; a coloured budget pill (% of cap,
green/amber/red) when a cap is set (script.js:92-109). |
LLM cost control at the front door. |
| Query Hit Rate | 24h hit-rate % (green ≥60/amber 30-60/red <30) + 24h & 7d counts +
median latency (renderHitRate script.js:149-160). |
The core "is retrieval working" signal. |
| Enrichment Coverage | Average of seven coverage fractions (quality/git/concept/log/endpoint/
sproc/ledgerId) as a headline %, plus a per-dimension line (renderCoverage :162-172). |
Shows how enriched each chunk is; low git/log explains thin why_query/churn. |
| Ledger Token Saving | −N% tokens (ledger vs direct file tools), green when
saving, plus "cheaper on X/Y" (renderBenchmark :124-141) from bench_* A/B. |
Quantifies ROI in tokens — the headline justification. |
| Graph Density | Distinct concepts, routes, sprocs (renderGraphDensity :174-179). |
How much cross-stack structure graphify materialised. |
| Caller Recall | % of source callers the graph resolves (green ≥70/amber ≥30/red <30) or n/a;
detail names the worst repo (renderCallerRecall :42-60). Dedicated
GET /api/caller-recall (Program.cs:582), off-thread. |
Honest self-audit — the graph is forward-only. |
| Learning Corpus | Agent-query count + sessions ingested + log templates + refactor plans
(renderCorpus :181-187). |
Tracks the compounding "has anyone tried this before" memory. |
| Hosted Session Ingest | State (off/pending/healthy/error) + last-run relative time, session
count, next run, interval; error text on failure (renderHostedIngest :189-212). |
Confirms the periodic transcript-ingest service is alive. |
| Repository Coverage chart | Doughnut of chunk count per repo (Chart.js) or empty-state
(renderChart :221-254); GET /api/distribution dist.byRepo
(Program.cs:569). | Which repos dominate or are under-covered. |
| Recent Architectural Ingests | List of the latest ingested files (repo tag, file, intent,
relative time) (renderActivity :256-275); GET /api/recent?limit=8
(Program.cs:637). | A live feed proving ingestion flows. |
| Query box + Trigger Ingestion | Free-text box posting to /api/ask
(Program.cs:992) for a synthesised cited answer (routes to
cluster_cohort/refactor_plan/find_similar/query_ledger), falling back to raw /api/search
(:650); "Trigger Ingestion" → POST /api/ingest (409 = already running); "View
code" → GET /api/graph/node-detail (:2886). |
A human uses the same brain the agents use, with verifiable citations. |
The dashboard is a single always-on health-and-ROI panel: freshness, spend, retrieval quality, token savings, and the honest caveats (caller recall, enrichment coverage) all sit in one view that anyone can watch without credentials.
Quality & Setup pages
The Quality page (quality.html) is a per-file risk table ranked by
Composite = Cyclomatic + Cognitive − Coverage×5, with a repo filter and sort
(risk/churn/coverage/complexity); rows link to /graph2?focus=<ledgerId> and pull
GET /api/quality?sort=&limit=100&repo= (Program.cs:2516). It is documented in
full on the Analysis & quality page. The Setup Guide
(setup.html) is a static onboarding/how-to-connect page whose only live behaviour is the shared
/api/stats status ping.
The weekly digest
Eight things the ledger already knows about the week — corpus size, which repos ingested, the route
inventory, which load-bearing files moved, where churn concentrates, how far the caller graph reaches, what is
still outstanding, and where the team is weak — live behind eight separate JSON endpoints that nobody reads on
a Monday. tools/weekly-digest.sh plus tools/weekly-digest.py assemble them into one
dated page under wwwroot/docs/digests/, with
latest.html as the pointer at the most recent one.
The design constraint is that a digest nobody can check is worse than no digest. Every figure on the page is read out of a response body captured at generation time, the endpoint is named under each heading, and a provenance table closes the page. There is no default, no estimate and no carry-forward from last week: when a fetch fails or a window is empty the section renders "No data from <endpoint> this week — <reason>", and the reason distinguishes a missing capture from an HTML error page from malformed JSON from a genuinely empty result. Two of the figures are deltas, which need a previous observation, so each run leaves a JSON sidecar beside the page and the next run diffs against it; the first run says it has nothing to compare with rather than presenting today's level as if it were a change.
The split between shell and Python is deliberate: the shell only GETs and writes bytes, so everything that
could be wrong about which week it is, which files are load-bearing, or whether a section has data at all
lives in weekly-digest.py under its own test suite — most of which assert that an
absence renders visibly.
2026-08-03T00:00:00Z → 2026-08-10T00:00:00Z),
generated 2026-08-09T12:46:23Z, and all 8 of its sections returned data. Treat its figures as a
snapshot of that moment, not as live numbers — it records 8 repositories indexed, because
infra was added after it was generated.The admin screen (admin.html)
On load, tryLoad() (:940-951) calls GET /api/admin/config
(Program.cs:1357); a 403 renders the gate card with a personalised
ssh -L 5006:localhost:<port> root@<host>, while success renders the panel and
applyConfig (:1490-1568) hydrates the fields. Save posts a diff-patch to
POST /api/admin/config (Program.cs:2332); Save & Restart also hits
POST /api/admin/restart (:2426). Every card below carries its own benefit.
Observability & cost cards
| Card | What it does | Why it matters |
|---|---|---|
| Gate card + SSH command | Explains loopback/tailnet-only access; prints the tailnet URL and an SSH-tunnel command auto-filled with host/port. | An off-tailnet operator still gets a one-copy path in. |
| Live Status (log tail + slowest files) | Idle/Running pill, live per-run detail (files, chunks,
reused, Groq calls/$, elapsed, "now: <file>"), a level-coloured log tail, and a top-10 slowest-files
table. GET /api/admin/ingest-status (Program.cs:2132) +
/api/admin/logs?since= (:1363), polled 1s running / 4s idle. |
Real-time ingestion observability including per-file hotspots. |
| Groq (config/spend/budget/reset) | API key (rotate, masked), model datalist, input/output
pricing per 1M tokens, lifetime Spend Cap (0=unlimited), lifetime readout + Reset spend totals.
GET /api/admin/spend (:2317), POST /api/admin/reset-spend
(:2374). | Full LLM cost governance. |
| Anthropic / Benchmark runner | Anthropic API key + model (default
claude-opus-4-8), "key set/no key" badge, Run benchmark (enabled only with a key).
POST /api/admin/bench/run?fresh=20 (:1825); seed /bench/seed
(:1794); list /bench/list (:1742). Feeds the Ledger Token Saving
tile. | Measures real ledger-vs-direct token cost on live discovery questions. |
| Top hit files | Every file path returned by query_ledger/find_similar, counted with hits, best
score, last-seen, last query; Refresh + Clear stats. GET /api/top-hits (:516),
clear /api/admin/top-hits/clear (:2366). |
"What is Claude actually pulling out." |
| Query analytics (Qdrant) | 24h hit-rate, 24h/7d counts, median latency, top failed-query
clusters, top hit paths; auto-refresh 30s. GET /api/query-stats (:562) from
motlogs_agent_queries. | Retrieval-quality analytics + raw material for synonym tuning. |
| Team metrics tile | Active devs, calls in window, reverted-rate, global LEDGER÷DIRECT
efficiency, a per-team table (devs, calls, calls/dev, cache-served, median top score) + durability line;
7/30/90d selector, 30s refresh. GET /api/admin/metrics/team?days= (:1777). |
Per-team adoption + reverted-session durability proxy (falls to shared until dev tokens
issued). |
| Query misses | Last 50 failed queries — zero (no rows) vs low-relevance (below threshold) —
with when/reason/hits/top-score/query. GET /api/query-misses (:540). |
The exact queries worth tuning for. |
Connection & build cards
| Card | What it does | Why it matters |
|---|---|---|
| Qdrant (config + clear) | URL, API key (masked), collection name; Clear Qdrant collection
(drops only the configured collection + local caches). POST /api/admin/qdrant/clear-collection
(:2181). | Vector-DB connection management + clean-slate reset. |
| MCP HTTP Transport | Shared Bearer token for POST /mcp (masked; blank=public);
low-relevance threshold (default 0.05); a Developer-tokens sub-tile issuing per-{developer,team} tokens
(shown once), listing, revoking. POST /api/admin/tokens (:1169),
GET (:1209), /revoke (:1226). |
Authenticates AND attributes every MCP call to a developer/team for the Team tile. |
| Fix dispatcher | Max-per-run, interval (minutes; restart-required), Daily auto-fix master
switch, Auto-merge fixed PRs (danger), and a read-only dry-run mirror of Git:DryRun. Manual:
POST /api/admin/agents/fix/dispatch (:1992). |
Governs the autonomous approve→PR pipeline and its safety gates. |
| Graphify | Enabled, run-on-ingest, binary path, output dir, snippet-footer toggle, LLM backend,
Reuse Groq (--backend openai with Groq creds). |
Configures the graph build powering /graph2, snippet footers, and cross-stack walks. |
Ingestion control room
The Ingestion glass is the operational cockpit: Sync All (iterate every repo, syncAll); per-repo
Sync + Clear (POST /api/admin/repos/clear :2205) with last-ingest timestamps; Max Parallelism;
Ignored Directories (globs) + Purge archived chunks (/api/admin/qdrant/purge-archived :2163); a
Repositories id→path list with an auto-ingest webhook badge (/api/admin/auto-ingest :689); Clear
caches (/api/admin/clear-cache :2358); post-ingest enrichment triggers (Run cross-stack / concepts
/ all post-passes — /api/admin/cross-stack/run :2021, /concepts/run :1471,
/post-passes :2043); a Source Snapshots list with download/delete
(/api/admin/source-snapshots :2245); and Graphs download-all zip + per-file list
(/api/admin/graph/files :1300, download :1321, download-all :1332).
Everything an operator needs to drive, throttle, enrich, back up, and reset the index lives in one card — the single control surface for the whole load pipeline.
Intelligence & governance cards
| Card | What it does | Why it matters |
|---|---|---|
| Synonyms | Run suggester (Groq over failed clusters), list with pending/approved/all filter,
approve/delete per row. /api/admin/synonyms/suggest (:1054), /list
(:1071), /approve (:1094), DELETE (:1124). |
Teaches query expansion (VRN = vehicle registration). |
| Per-repo agents.md | Generate all + per-repo Preview. /api/admin/agents-md/generate
(:1412), /preview (:1424), /export (:1438). |
Auto-authored per-repo agent onboarding docs, at no LLM cost. |
| Concept clusters | Re-cluster (k-means over design_vector → concept_id) + List centroids.
/api/admin/concepts/run (:1471), /list (:1487). |
Powers cross-language concept_neighbours and refactor planning. |
| Cross-stack edges | Resolve all (routes↔fetch, sproc calls↔CREATE PROCEDURE) + Peek one path.
/api/admin/cross-stack/run (:2021), /peek?path= (:2116). |
Materialises the controller↔SQL↔UI links. |
| Refactor plan (+ drift check) | Goal + anchor + depth → structured blast-radius plan JSON;
Drift check compares a changed-file list to the plan. POST /api/plan (:1013). |
PR-attachable, risk-annotated cross-repo refactor plans. |
| Session transcripts | Days-back + Ingest sessions into motlogs_session_outcomes.
POST /api/admin/session-ingest?days=N (:1392), status /status
(:1402). | Manual trigger for the "has anyone tried this before" corpus. |
| Skill candidates | Clusters recurring sessions into proposed MCP tools; repo scope, sort, Run
discovery, Refresh, per-row Promote. /api/admin/skill-discovery (:1506), /list
(:1523), /preview (:1538); promote /api/admin/skills/promote
(:1573). | Surfaces repetitive agent tasks worth promoting. |
| Skills & Agents — promotion registry | Governed discover→propose→review→approve→publish→
deprecate lifecycle; propose-from-scratch form; filters; per-row transitions (approve runs a validation
gate). GET /api/admin/skills (:1555), POST propose (:1596),
/transition (:1628), /export (:1688). |
Governance that turns candidates into published .claude/skills. |
| Agent console — findings | The shared findings queue (inconsistency/bug/fix-proposal/test-gap/
dead-code/log-bug/improvement) with repo/type/status filters; Run agent buttons; per-row approve/dismiss;
markdown Preview. Runners /api/admin/agents/{inconsistency,bug-hunt,test-gap,conversation-review,
log-scan,dead-code}/run (:1892-1961), fix/run (:1976), fix/dispatch
(:1992); queue /api/admin/findings/list (:1728), /status
(:1870), /preview (:2005). |
The human-gated triage surface for the multi-agent quality platform. |
| Global action row | Save Settings, Reload Current, Save & Restart Service
(/api/admin/restart :2426). | Applies or reverts config, with an explicit restart path. |
Every intelligence layer the ledger builds — synonyms, concepts, cross-stack edges, refactor plans, session memory, skills, and the agent quality queue — has a hands-on control here, so the whole platform is operable by a human, not just by background loops.
API endpoints present but not surfaced as tiles
For completeness, several endpoints exist without a dedicated UI card:
GET /api/whats-new (Program.cs:2623); GET /api/learning-needs
(:602) and /api/admin/learning-actions (:1148);
POST /api/admin/qdrant/rebuild (:774), /qdrant/gc (:2868),
/cache/snapshot (:2800), /deprecate (:2386);
GET /api/admin/query-cache-stats (:2419);
GET /api/admin/eval/relevance (:2818), smoke-run status (:2791)/run-now
(:2839), /api/admin/journalctl-tail/status (:2781);
POST /api/admin/rubric/backfill (:2067), /decisions/extract
(:624), /conversation/ingest (:631);
GET /api/graph/{quality,churn,concepts} (:2448/2472/2493),
/graph/data/* (:932), /graph/data/similar (:1259),
/graph2 + /graph2/app.js; POST /mcp + GET /mcp
(:1586/1631); POST /api/admin/overlay/register and
/api/admin/overlay/refresh (checkout-overlay registration and the PostToolUse-hook-driven
re-diff, loopback-only — see MCP tools & deploy); and the public
GET /api/gate-health (the mot-ledger consult-gate health tile — see
Governance layer).
Open Brain — the MOTMaster Architectural Ledger · generated from source 2026-07-07