--- title: integrity-dashboard created: 2026-07-07 updated: 2026-09-15 type: entity tags: [infrastructure, sdk] confidence: high source_files: - integrity-dashboard/src/App.tsx - integrity-dashboard/src/Dashboard.tsx - integrity-dashboard/src/pages/AuthPage.tsx - integrity-dashboard/src/pages/SettingsPage.tsx - integrity-dashboard/src/pages/IdentityPage.tsx - integrity-dashboard/src/pages/IntelligencePage.tsx - integrity-dashboard/src/pages/HealthPage.tsx - integrity-dashboard/src/pages/ShieldPage.tsx - integrity-dashboard/src/components/shield/ShieldFleetOverview.tsx - integrity-dashboard/src/services/shieldBackend.ts - integrity-dashboard/src/pages/FinancialsPage.tsx - integrity-dashboard/src/components/cortex/CortexOperationsTab.tsx - integrity-dashboard/src/pages/DeveloperPage.tsx - integrity-dashboard/src/pages/WikiPage.tsx - integrity-dashboard/src/context/DashboardContext.tsx - integrity-dashboard/src/context/SettingsContext.tsx - integrity-dashboard/src/services/oracle.ts - integrity-dashboard/src/services/userapi.ts - integrity-dashboard/src/services/graphMemory.ts - integrity-dashboard/src/types/graphMemory.ts - integrity-dashboard/src/components/ui/DIDExplorer.tsx - integrity-dashboard/src/components/tabs/ActuarialHub.tsx - integrity-dashboard/src/components/observability/TraceAnalysisPanel.tsx - integrity-dashboard/playwright.config.ts - integrity-dashboard/e2e - integrity-dashboard/demo/src/integrity_demo/seed_audit.py - integrity-dashboard/demo/tests/test_seed_audit_chain_scope.py - integrity-dashboard/Dockerfile - integrity-dashboard/.dockerignore - integrity-userapi/app/config.py - docker-compose.yml --- **This page was rewritten on 2026-08-13.** The prior content described an older, structurally different dashboard (`AgentListPage`, `MarketsPage`, `WalletPage`, `wagmi`/`viem`, a Notion-style `react-grid-layout` widget dashboard) that no longer exists on disk — it predates the 2026-08-12 "Reconcile integrity-dashboard with integrity-mvp's current state" commit, which replaced the tracked snapshot with `integrity-mvp`'s current, independently-developed state. None of the file paths the old version of this page cited exist anymore. Per this wiki's "no aspirational content" rule, the old content is replaced rather than patched. ## Table of contents - [What this is](#what-this-is) - [Container packaging](#container-packaging) - [Routes](#routes) - [Browser registration recovery and bond gate](#browser-registration-recovery-and-bond-gate) - [Agent 360 data boundary](#agent-360-data-boundary) - [Cortex Operations tab boundary](#cortex-operations-tab-boundary) - [2026-08-13 full-site Playwright audit](#2026-08-13-full-site-playwright-audit) - [Real bugs found and fixed this pass](#real-bugs-found-and-fixed-this-pass) - [Known gaps not fixed this pass](#known-gaps-not-fixed-this-pass) - [Live deployment and rendered verification (2026-09-14)](#live-deployment-and-rendered-verification-2026-09-14) - [Rendered control-center validation (2026-09-15)](#rendered-control-center-validation-2026-09-15) - [Local e2e stack](#local-e2e-stack) ## What this is The lint toolchain declares the `globals` package explicitly in `devDependencies`; this keeps the flat ESLint configuration reproducible in Continuous Integration (CI) and after a clean `npm ci`. React 18 + TypeScript + Vite 8 dashboard, `react-router-dom` v7. 22 routes (`src/App.tsx`), no route-level auth guard — `MainAppLayout`/`PublicLayout` render `` unconditionally, and individual pages read auth state (`getToken()` in `services/userapi.ts`, a JWT in `sessionStorage`) ad hoc. Most pages are driven by `DashboardContext`'s `selectedAgent` (populated from a real `oracle.listAgents()` call, no mock filter) and read real data from three backends: `integrity-oracle` (`services/oracle.ts`), `integrity-userapi` (`services/userapi.ts`), and, for a few chain-reading components (`StakingPanel`, `CreditPanel`, `PrivacyPanel`, `TokenWallet`, `ActuarialHub`, `HealthPage`), direct `ethers.JsonRpcProvider` reads against addresses hardcoded from the committed `src/deployments.baseSepolia.json` — **those chain reads always target real Base Sepolia**, never whatever `RPC_URL` the oracle backend itself is configured with, so an agent registered only on a local anvil chain (as in local e2e testing) shows real, honest empty states on those specific panels even though oracle-backed panels for the same agent show real data. `HealthPage`'s own banner states this explicitly: "Smart BAA registry, EHR Gates, and Quarantine are all real (Base Sepolia)." ## Container packaging The Compose `dashboard` service builds from `integrity-dashboard/` using `node:22-alpine`. Its Dockerfile installs the exact committed dependency graph with `npm ci`, then runs the Vite development server on `0.0.0.0`. The build context excludes host `node_modules`, demo `.venv`/Python caches, `dist`, Playwright/test output, local environment files, caches, and repository metadata through `.dockerignore`; host dependencies therefore cannot overwrite the clean in-image install during `COPY . .`. On 2026-09-01, a cold `docker compose build --no-cache dashboard` completed with a 4.88 MB context and installed 451 packages with 0 reported vulnerabilities. A baseline build from a checkout without `.dockerignore` had sent 717.42 MB when host dependencies were present. A container launched from the rebuilt image reached Vite readiness in 713 ms and served the real application shell over HTTP before it was removed. This is local build/runtime evidence, not evidence that a persistent or remote dashboard deployment was replaced. ## Routes | Route | Page component | Backend dependency | |---|---|---| | `/` | `LandingPage.tsx` | none — static | | `/auth` | `pages/AuthPage.tsx` | `userapi` (login/register), `DashboardContext.connectWallet` | | `/wiki` | `pages/WikiPage.tsx` (lazy) | none — reads build-time `src/generated/wiki-data.json` | | `/docs`, `/privacy`, `/terms` | `pages/{Docs,Privacy,Terms}Page.tsx` | none — static, under `PublicLayout` | | `/dashboard` | `Dashboard.tsx` | `oracle` (AIS, stake, history, telemetry, audit log, traces) | | `/identity` | `pages/IdentityPage.tsx` | `DashboardContext` only, no direct fetches | | `/financials` | `pages/FinancialsPage.tsx` | `oracle` + chain (`TokenWallet`, `StakingPanel`, `CreditPanel`, `ActuarialHub`) | | `/intelligence` | `pages/IntelligencePage.tsx` | `DashboardContext`; custom telemetry fields are `localStorage`-only | | `/prediction-markets` | `components/tabs/ActuarialHub.tsx` (`mode="markets"`) | `oracle.listMarkets()` + chain writes via `SovereignAgent.execute` | | `/health` | `pages/HealthPage.tsx` | `oracle` (NHI governance) + chain (BAAs/EHR gates/quarantine, real Base Sepolia) | | `/shield` | `pages/ShieldPage.tsx` | `ShieldFleetOverview` reads the real Shield backend; `Live Attack Demo` is a separate synthetic/local simulator tab | | `/cortex` | `pages/CortexPage.tsx` | Unified Cortex workspace: timeline, graph, recall, inference, integrity, and operations tabs backed by `xibalba-cortex`'s `local_api.py`, `VITE_GRAPH_MEMORY_URL`, default `:8420` | | `/memory` | compatibility redirect | Redirects to the canonical `/cortex` workspace so existing bookmarks remain valid | | `/developer` | `pages/DeveloperPage.tsx` | `oracle` + chain (IDE/contracts tab), `oracle` (Trace Analysis tab) | | `/settings` | `pages/SettingsPage.tsx` | `userapi` (API keys), `DashboardContext` (theme/layout), chain (`PrivacyPanel`) | ## Browser registration recovery and bond gate `RegisterAgentModal.tsx` uses the currently authorized Base Sepolia `AgentPrimitivesFactory` from the dashboard deployment mirror and performs the same load-bearing sequence as the SDK: deploy `SovereignAgent`, deploy `StateAnchor`, fund the agent with the 100 ITK registration bond, grant the oracle anchor role, anchor the non-zero genesis memory root through `SovereignAgent.execute`, approve the factory bond through the same account, atomically register the seven primitives, then submit the chain-verified record to the Oracle. The read-only preflight checks the factory role, domain admission, and combined wallet/agent ITK needed for the bond before enabling a fresh deployment. Confirmed browser steps are stored in a chain-versioned local progress record scoped to the connected controller, so a browser or workstation restart reuses deployed contract addresses instead of silently orphaning them. `scripts/sync-chain-bytecode.mjs` regenerates the browser deployment ABI and bytecode directly from current Foundry artifacts; `npm run sync-chain-bytecode` must be run after either browser-deployed contract changes. ## Agent 360 data boundary The dashboard's `/dashboard` overview joins the selected Oracle agent DID with real Shield and Cortex records. Shield uses the DID as its tenant key and returns devices, policy/decision counts, and enforcement posture through `services/shieldBackend.ts`. Cortex exposes `GET /api/agent/{agent_id}/summary`, backed by `sources.agent_id`, returning agent-attributed memory, embedding, session, provenance-source, and recent memory counts. The Agent 360 panel renders missing attribution as an explicit empty state; it does not infer ownership from global Cortex totals. Cortex runtime ingestion must emit the registered Oracle DID through `XIBALBA_AGENT_ID` and use `XIBALBA_CORTEX_IDENTITY_MODE=full` when the operator needs cross-system DID joins. Historical pseudonymous or legacy records remain unchanged and are not silently reassigned. **`/shield`** renders two explicit surfaces: `ShieldFleetOverview` is the default real-backend evidence view, reading dashboard summaries, detection quality, enforcement outcomes, exporter status, device state, integrations, and the 3D evidence graph through `services/shieldBackend.ts`; the `Live Attack Demo` tab is a separate synthetic/local demonstration of the Tier-2 escalation path. Backend data is not replaced by simulator data when the service is unavailable; the fleet view shows an unavailable or partial state. The local integration overlay exposes the backend on `:8765`, but healthy local responses remain development evidence and do not establish production sensor, Oracle, or live-chain proof. ## Cortex Operations tab boundary The `/cortex` workspace's **Operations** tab is a focused operator surface over the separate `xibalba-cortex` local API. It does not move canonical memory ownership into `integrity-core`: Cortex's profile-scoped SQLite store remains authoritative for Cortex memory, while Integrity protocol packages remain independent of that external repository. The page exposes four implemented groups from `services/graphMemory.ts`: - evidence-backed hybrid retrieval (`POST /api/retrieval/hybrid`) followed by persisted trace readback (`GET /api/retrieval/trace/{id}`), including channel state, root hash, result signals, the first result's Merkle inclusion proof (`GET /api/retrieval/trace/{id}/evidence?rank=1`), and an optional projection checkpoint link; - proposed extraction review (`GET /api/extraction-proposals`) with explicit accept/dismiss decisions (`POST /api/extraction-proposals/{id}/decision`); - pending inference-task and embedding-model visibility (`GET /api/inference/tasks`, `GET /api/embedding/models`); and - checkpoint list/create, reconciliation, and rebuild for the `memories`, `entities`, and `relations` projections under `/api/projections/{id}`. The surface shows a partial-view warning when any operator API is unavailable and does not synthesize placeholder records. Retrieval can explicitly report degraded channels. Extraction acceptance and projection rebuild are real writes to the configured Cortex service; the dashboard currently supplies `decided_by: "integrity-dashboard"` but adds no user authentication or operator authorization of its own. The page therefore disables extraction decisions and projection mutation outside a loopback-hosted dashboard. This browser-side guard is defense in depth, not authentication: keep the Cortex API itself local or behind an authenticated operator boundary before exposing it beyond a trusted development environment. The workspace's core loader settles statistics, status, integrity links, sessions, graph data, and the inference manifest independently. A failure in an optional capability therefore produces a named partial-data warning without discarding a successful `/api/sessions` response. ## 2026-08-13 full-site Playwright audit The 2026-08-13 audit covered 16 routes and 140 tests. The current tree has 19 Playwright specs and 137 test declarations, while `src/App.tsx` defines 22 route entries including redirects and static/legal pages. The historical audit had a comprehensive Playwright spec under `e2e/` (one file per page/route, `landing.spec.ts` through `memory.spec.ts` — 140 tests total), written and run against a real local backend stack, no mocking, following each spec with a full-page screenshot reviewed before moving to the next page. This replaced 5 pre-existing specs (`dashboard.spec.ts`, `health.spec.ts`, `shield.spec.ts`, `wiki.spec.ts`, plus `memory.spec.ts`) written under a looser discipline, and added 11 new ones. `smoke.spec.ts` (all 14 non-wiki/non-memory routes, zero-console-error only) is kept as a fast pre-existing cross-check. ## Real bugs found and fixed this pass Each was caught by a Playwright assertion failing against real rendered output, then fixed in the app (never by loosening the assertion): - **`WikiPage.tsx`** — `inlineMarkup`'s bold-markdown regex matched `\*\*[^*]+\*` (one closing asterisk) instead of `\*\*[^*]+\*\*` (two), so `**bold**` rendered as literal asterisks on every one of the 38 wiki pages. Fixed the regex. - **`DIDExplorer.tsx`** — the DID-identifier flex child had no `min-width: 0`, so its un-truncated intrinsic text width (despite its own `text-overflow: ellipsis`) inflated the parent grid's `1fr 1fr` track sizing, squeezing the sibling "Sovereign Node" status-badge column down to ~103px and mashing its label/value pairs together unreadably. - **`integrity-userapi/app/config.py`** — `cors_origins` was missing the Playwright dev-server origin (`127.0.0.1:5189`, distinct from `localhost:5189` for CORS purposes), blocking every real login/register test. Added both forms. - **`AuthPage.tsx` / `DashboardContext.tsx`** — `handleWalletAuth` navigated to `/dashboard` unconditionally after `connectWallet()`, even when connection failed (no injected wallet) — `connectWallet` swallowed all failures internally and never signaled them to the caller. Changed it to return a `boolean`, and gated the navigation on it. - **`IntelligencePage.tsx`** — `FormulaCard`'s formula box used `overflow: hidden` with `justify-content: center`, so a formula wider than its box was clipped on **both** edges — `"AIS = (...)"` rendered as the unreadable `"[S = (...)"`. `overflow-x: auto` alone wasn't sufficient either (a centered flex item inside an auto-overflow container starts pre-scrolled to the midpoint); fixed with `justify-content: flex-start` plus `overflowX: auto`. - **`Dashboard.tsx`** — the AIS radar chart mapped `ais.components.*` (0–1000 scale, matching `integrity-oracle/scoring-core`'s `MAX_COMPONENT_SCORE`) with `* 100`, as if it were a 0–1 fraction — producing values like 100,000 against a `fullMark: 100` domain, which made Recharts render a literal `"NaN"` tick/label. Changed to `/ 10`. - **`TraceAnalysisPanel.tsx`** — collapsed "genuinely still loading", "no agent selected", and "agent has zero traces" into one `selectedSession === null` check, permanently showing `"Loading session..."` for the common no-telemetry-yet case — indistinguishable from a hang. Added explicit `status` state (`'loading' | 'no-agent' | 'no-traces' | 'ready'`) with honest, distinct copy per case. - **`ActuarialHub.tsx`** — `resolve_deadline` is a real ISO 8601 string from the oracle (`services/oracle.ts`'s `MarketSummaryDto`, confirmed live: `"2026-07-09T19:22:04Z"`), but the Live Markets table parsed it with `Number(m.resolve_deadline) * 1000` — `NaN` for an ISO string — rendering the literal text `"Invalid Date"` in the Deadline column, and silently making the derived `past` flag permanently `false` (a `NaN` comparison is always false), which broke the creator-resolve button gate for any market actually past its real deadline. Changed to `Date.parse(...)`. Infra fixes made to enable this testing, not application bugs: the root `docker-compose.yml`'s `mvp` service references `../integrity-mvp`, which no longer exists on disk (folded into this directory); worked around by starting only the specific services needed (`postgres redis opa oracle-backend bcc-middleware userapi-postgres userapi`) rather than the full `make up`/`make up-local`. ## Known gaps not fixed this pass Flagged as findings, left alone per scope (test/bugfix pass, not a feature or redesign pass): - **`HealthPage.tsx`** declares a `TABS` array (Smart BAAs / EHR Gates / Audit & Compliance / Quarantine) and imports `SubTabs`, but never renders `SubTabs` or gates any section behind an active-tab check — every section is unconditionally stacked on one continuous page. Dead code, not a functional bug (nothing crashes or hides content). - The Shield fleet surface is locally wired to the real backend client, but production registration, sensor coverage, Oracle readback, and burn-in remain external/runtime evidence gates. ## Live deployment and rendered verification (2026-09-14) The local live stack was rebuilt and recreated for `dashboard` and `userapi` after adding `http://127.0.0.1:5173` to the user API CORS allowlist. The published containers served the current Integrity MVP landing page and its `/auth` route. A headless Playwright journey verified HTTP 200 rendering, non-empty meaningful DOM content, the `Launch MVP Dashboard` navigation, and a mobile viewport render. The only remaining browser console responses were expected `401 Unauthorized` results from unauthenticated `/me` requests; no CORS errors remained. This is local container and rendered-runtime evidence, not proof of a remote deployment. The exact source and runtime provenance remains recorded in the dated handoff runbook. ## Rendered control-center validation (2026-09-15) The dedicated validation configuration in `integrity-dashboard/playwright.validation.config.ts` runs regular headless Playwright against the local Vite viewer and records screenshots, DOM assertions, console output, network findings, axe-core results, and reports. The in-app Browser runtime was unavailable for this pass, so it is not counted as a second browser evidence surface. The final desktop Chromium route audit passed 33/33 declared routes. The protocol route set passed 27/27 responsive route-viewport checks at desktop, tablet, and narrow mobile widths, and safe refresh/copy controls passed at all three widths. The validator also scans rendered output for private keys, seed phrases, and recovery material; none were found. Authenticated isolation is implemented but requires a disposable userapi principal with two exact agent DIDs already assigned through `/me/agents`. The test is deliberately skipped when those variables are absent and must not be reported as browser-verified in that state. Strict axe blocking and real wallet/contract writes remain separate release gates. See the dated [dashboard handoff](../../archive/2026-09/INTEGRITY-DASHBOARD-HANDOFF-2026-09-15.md) and the [integration report](../../integrity-dashboard/docs/INTEGRATION-REPORT.md) for commands, evidence paths, and limitations. ## Local e2e stack The labeled dashboard-audit seeder writes Oracle primitive and leaderboard cache rows with the explicit `CHAIN_ID` selected for the run (Base Sepolia `84532` by default). Its leaderboard rows and freshness marker use the chain-scoped conflict keys introduced by Oracle migration `0017`; rerunning the seeder therefore cannot create unknown-provenance primitive rows or fail against the migrated composite leaderboard key. `playwright.config.ts`'s `webServer` boots `npm run dev -- --port 5189` against whatever `ORACLE_URL`/`USERAPI_URL`/`BCC_MIDDLEWARE_URL` `.env` resolves to — it does **not** stand up a backend itself (no `e2e/global-setup.ts` exists in this repo, despite `docs/TESTING.md` historically describing one; see that page's own update). To run the full suite against real data: ```bash make chain # local anvil + genesis deploy (deployments.local.json) docker compose up --build postgres redis opa oracle-backend bcc-middleware userapi-postgres userapi # `docker-compose` (hyphenated binary) is not installed in every # environment; `docker compose` (the plugin) is the portable form cd integrity-dashboard && npx playwright test ``` For `/cortex`, separately run `xibalba-cortex`'s `local_api.py` (a different repo): `uv run python -m xibalba_cortex.local_api --home --allowed-origin http://127.0.0.1:5189`. Point it at a fresh, empty `--home` directory for testing rather than a real profile — its own background session-capture means even a fresh store won't reliably stay at `0` records, so specs should assert real non-fabricated counts, not a literal zero. Related: [integrity-oracle](integrity-oracle.md), [integrity-userapi](integrity-userapi.md), [AIS API spec](ais-api-spec.md) (the field-shape source of truth `oracle.ts` is checked against).