inventory.md
c134 Phase 0: inventory of both apps
- 1. Side by side
- 2. app.qpoint.io mapped
- 3. qdash UI mapped
- 4. The qdash backend: 38 `/api/*` routes (`web.rs` lines 59–132)
- 5. What moves, what is replaced, what is dropped
- 6. The API gap list
- 7. Backend asks, consolidated
- 8. For c135: what the web app needs that the layer (0.9.37) does not have
- 9. Open questions for Tyler and Kamal
As of: 2026-11-09. Read from the trees, no servers started. Sources: ../app.qpoint.io (last code commit a6fae09, 2026-03-12; later commits are layer bumps), ../qcontrol/crates/qdash/ui on feat/new-pages (app last touched 4af4df15, 2026-10-02), ../qcontrol/crates/qdash/src/web.rs (a7de3f68a, 2026-09-26, 11,680 lines). Rules referenced: the bible ui/docs/data-interface.md (R13, R15, R23, R25, R31) and ui/LAYER-CANDIDATES.md.
Reader: Mark; Tyler and Kamal for §6–§9; the c135 executor for §8.
Shape of the finding: app.qpoint.io is a frame (sign-in, orgs, members, contexts, settings, install) around traffic pages that are being retired; qdash is pages (44, one data discipline) with no frame at all. The seam is exactly where the plan put it (D1).
1. Side by side
| app.qpoint.io | qdash UI | |
|---|---|---|
| Stack | Nuxt 4 SPA (ssr: false), JavaScript composables, Pinia, @qpoint-io/q-nuxt-layer (latest), Firebase 11, chart.js, d3, headlessui | Nuxt 4 SPA, TypeScript everywhere, no Pinia (shell singletons, bible R22), layer pinned by lockfile (0.9.36), vitest on lib/ |
| Routed pages | 76 (app/pages, excluding components/, dev/, lib/): orgs 28, stacks 10, inventory 9, traffic 7, alerting 6, security 4, sso 4, session 4 | 44 (app/pages): inventory 16, organization 6, policies 2, integrations 2, activity 2, 16 single pages; /overview, /reports, /settings, /account are DesignInProgress placeholders |
| Layouts | default (logo + account menu), global (PageMain + section left nav + teleported filter slot), org, session (logo over a white card), dev | one: app/layouts/default.vue = AppSidebar + BackendBanner + UxStickyBand + GlobalFilterBar + ToastStack |
| Auth | Firebase Auth: email, Google, GitHub, Microsoft popups, SSO by email domain (connectors collection), SAML and OIDC through $api + signInWithCustomToken; auth.global.ts redirects to /sign-in, keeps pre-auth path | none; web.rs require_local_host refuses non-loopback Hosts; no viewer identity (/account comment: no /api/me) |
| Scoping | URL query orgId / contextId / stackId / endpointId → useContext(); context.global.ts mirrors orgId/contextId into the auth store (localStorage) | none; one fleet; filters and ?period= in the URL (useGlobalFilter) |
| Data access | three injected clients: $pulse (Pulse /api/v1, bearer from /jwt/tokens, per-org pulseUrl from orgs/{id}/settings/hosting), $api (qpointApiUrl, token from users/{uid}/orgs/{orgId}.apiToken), $warehouse; Firestore live onSnapshot via useList / useEntity | one client $qdash (plugins/qdash.ts, relative /api, 8 s timeout, reports to useBackendHealth); Transport = http() or local(key) (composables/api/transport.ts); primitives useList / useResult / useEntity poll at 3 s |
| Contract | none written; field names live in composables and components | 28 type files in types/api/ written from web.rs (R13); 46 domain modules; 51 rollup specs in lib/ |
| Time | pulse store timeframe (24hrs default) in localStorage; timeFrameToStart | the when: pill: ?period= all / 24h / 7d / 30d, default 7d (filter-keys.ts; 30d at the port, c128) |
| Honest states | none (lists default to [], loaded flag only) | R31: StateEmpty, SkeletonRows, usePageState, BackendBanner, ToastStack (c108) |
| Wizards | the kit in app/components/wizard/ + utils/sequin.js; seven flows | none; the integration setup modal is a stepper of its own (components/alerts/IntegrationSetupModal.vue) |
| Alerting | Pulse-backed rules, events, integrations, scanners (composables/alerting/) | "would fire" computed client-side from five feeds (composables/alerts/alerts.ts); routing persisted (/api/channels); delivery projected |
| Build and serve | Cloudflare Pages SPA, Firebase project, pulse.qpoint.io, firebase.qpoint.io functions | dist/ committed and embedded in the qdash binary; dev proxies /api to :7878 or QDASH_API_BASE |
2. app.qpoint.io mapped
2.1 The frame
- Sign-in and sign-up (
app/pages/sign-in.vue,sign-up.vue,session/components/Oauth.vue): email + password, three OAuth popups, anduseSSO(email)which queriesconnectorsby the email's domain and swaps the form for "Sign in with SSO" →/sso/<provider>/sign-in?domain=./forgot-passwordis linked but no page exists. On successdataLayer.push(GTM) then the pre-auth path or/. - SSO (
app/pages/sso/,server/routes/sso/saml/):saml/sign-inasks$api/sso/saml/authforauthUrland redirects; the IdP posts to the Nitro routeassert.ts, which 302s tosaml/callback?assertion=; the callback posts/sso/saml/tokenand signs in with a Firebase custom token.metadata.xml.tsredirects to the API's metadata. OIDC is the same dance with/sso/oidc/authand/sso/oidc/token(code,state,redirectUri). Per-org settings inorgs/{id}/settings/sso{provider, domain, url, config} (settings/sso.vue,sso/components/provider/OIDC|SAML.vue). - After sign-in (
app/pages/index.vue): a three-stage wait on Cloud Functions: profile created (users/{uid}), invitations accepted (users/{uid}/invitations), permissions synced (users/{uid}/orgs/{orgId}.roles/.permissions); then/orgs/newor/orgs/overview?orgId=. - Org and context (
stores/auth.ts,composables/context.js,middleware/context.global.ts): the org is a query param, remembered in localStorage; the Pulse token is re-minted per (org, context) through/jwt/tokens{service, contextId, role}; the org switcher (orgs/components/Selector.vue) and the "View:" context selector (contexts/Selector.vue) sit in the top nav (layouts/components/nav/Top.vue) withPageMessagesand the user popover (nav/User.vue: Account, Contact Us, Sign Out). - Permissions (
composables/user/orgs.jsuseOrgPermissions): twelve strings read from the user-org doc (traffic.view,security.view,payload.view,log.view,org.config,org.manage,org.team,traffic.manage,security.manage,context.manage, …); roles are chosen as checkboxes on invite (settings/team/manage.vue, save text "Invite"). - Self-hosting hint already present:
orgs/{id}/settings/hostingholdspulseUrl,pulseSecret,warehouseUrl,screenshotUrl(composables/org/settings/hosting.jsuseSelfHostedSettings); the Pulse plugin points at it when set.
2.2 Data access
| Store | Collections and documents (Firestore, composables/firebase/crud.js useList / useEntity) |
|---|---|
| user | users/{uid} (profile), users/{uid}/orgs/{orgId} (roles, permissions, apiToken, pulseToken, warehouseToken), users/{uid}/invitations, users/{uid}/orgs/{orgId}/contexts, connectors (SSO by domain) |
| org | orgs/{id} (name, status, ready, relayToken, certificateAuthority, installed…), orgs/{id}/members, orgs/{id}/contexts/{cid}/members, orgs/{id}/settings/{hosting,sso,deploy,proxy,tls,endpoints,jwt,wiretap,stack}, orgs/{id}/deployments/{did}/instances (name, type, token, active), orgs/{id}/tasks (onboarding checklist), orgs/{id}/tags, orgs/{id}/services (adapters Pulse / Qscan / S3 / Warehouse), orgs/{id}/releases, orgs/{id}/stacks/{sid}/… (adapters, rules), wiretap filters |
| Client | Routes used ($pulse.fetch → <pulseUrl>/api/v1, composables/pulse/crud.js and metrics.js) |
|---|---|
| Pulse | /alerts (+`/{id}/enable |
| API | /jwt/tokens (POST), `/sso/saml/auth |
2.3 Pages by section (nav in pages/<section>/components/Nav.vue)
| Section | Pages | Standing for the port |
|---|---|---|
| Session | sign-in, sign-up, sign-out, me (profile + delete account), contact, sso ×4 | frame: moves (re-homed on the new identity) |
| Orgs | index (create or join), new, configure (deploy preferences), install (qtap wizard), overview (onboarding checklist, four summary tiles, quick actions), settings ×17: Organization, Capabilities, Team, SSO, Views, Config, SSL/TLS, Endpoints, Connect (JWT tokens), qtap Config + Filters, Tokens (deployments), Tags, Services, Releases, Snapshot (generated qpoint.yaml), MCP Clients | frame: moves; the proxy/TLS/qtap-config settings go with the stacks decision |
| Inventory | vendors, clients, endpoints, api-tokens, geolocation (+ sensitive-data link) | replaced by qdash Inventory and Organization |
| Traffic | performance, availability, bandwidth, connections, HTTP requests (Pulse metrics) | dropped (qproxy traffic model) |
| Security | overview (vulnerability scanners), data-risk (sensitive data flow) | replaced by qdash Findings, Posture, DLP |
| Alerting | events, rules, integrations ("Webhooks") | re-homed (D7) into qdash Alerts + Integrations |
| Stacks | adapters (plugins), config rules, routing, settings | dropped unless qproxy returns (question for Tyler) |
| dev, lib | component showcase (/dev/*, /lib/*) | design site's job; not ported |
2.4 Alerting (composables/alerting/, pages/alerting/components/)
Rules are Pulse entities {name, description, enabled, kind, config, query, checkInterval, reportFrequency} with four kinds and an adapter form each: threshold (metric, operator, threshold, group by), delta (metric, increase/decrease %, group by), discover (new vendor / endpoint / client / PII / token), risk (source and destination define-by, risk labels). Templates or custom (rules/Edit.vue). Integrations are Pulse entities with one UI adapter, Webhook (method, URL, token); rules link integrations through /alerts/{id}/integrations. Events (events.js) join /events/alerts with the rules and draw lines per alert. Scanners (scanner.js) are the vulnerability side. qdash's counterpart: lib/catalogs/connectors.ts + lib/integration-setup.ts recipes (Slack, PagerDuty, email digest, webhook previews), /api/channels routing, /api/integrations config, no engine and no sender.
2.5 Wizards
The kit (app/components/wizard/: Steps, EduHarness, PingListener, ToggleHarness, FinalStep; utils/sequin.js) was replicated by c125 into design app/components/replicas/wizard/ (plus the mask-image radar) and demoed at /pages/phase4/wizard-kit; it is still not in the layer. Flows: Install Qtap (orgs/components/install/qtap/: Name → Choose your Environment → Installation with Docker / Kubernetes / Linux instructions → wait for first traffic (deployment.active) → Installation Successful; mounted by orgs/install.vue on the default deployment's token), Install Qscan (Environment → Install, Docker / Kubernetes), Deploy Qproxy and Connect an app (dev mocks only: dev/components/mock/wizards/), the onboarding checklist (orgs/{id}/tasks: QtapDeploy, QscanDeploy, S3Configure; onboarding/Checklist.vue), preferences (Capabilities, Hosting), create a View (Name → Filter → Members), the security activate wizards (data types, stacks, persistence; scanner categories), MCP configure (Claude / Codex / Other instructions).
3. qdash UI mapped
3.1 Shell
app/layouts/default.vue: AppSidebar (three sections MONITOR / GOVERN + SECURE / ADMIN, disclosures per c109, footer with Working and Experimental toggles and the theme toggle), BackendBanner (unreachable → one banner; 2+ missed polls → "last updated" tag), UxStickyBand with the title pinned beside the filter (c104/c106), GlobalFilterBar (the one layer Filter; keys from composables/shell/filter-keys.ts FILTER_KEYS: team, provider, user, host, agent, posture, kind, source, status, flagged, attention, server, model, workspace, adapter; the permanent when: pill), ToastStack. / is the index of micros (pages/index.vue, c109); middleware/team-routes.global.ts redirects moved routes (lib/moved-routes.ts). Shell composables auto-import; domain and primitives import explicitly (bible §14).
3.2 Pages → composables → routes
| Page(s) | Domain module | Routes |
|---|---|---|
/ index | fleet/indexSummary | the listings' own feeds |
/inventory/harnesses (+detail), /inventory/agents/detail | inventory/harnesses, inventory/agents | /api/harnesses, /api/agent |
/inventory/providers (+detail, accounts/detail) | inventory/providers, inventory/accounts | /api/providers, /api/accounts |
/inventory/models, mcp-servers, skills, tools, workspaces (+details) | inventory/* | /api/models, /api/mcp-servers, /api/skills, /api/tools, /api/workspaces; details add inventory/surface → /api/surface |
/organization/users, teams (+details) | team/people (seven feeds), team/teams, fleet/costs | /api/sessions, /api/teams (GET/POST), /api/costs, … |
/organization/endpoints (+detail) | team/endpoints | /api/endpoints (?host= detail mode) |
/activity (+detail) | team/sessions | /api/sessions (?run= detail) |
/live | live/* (8 modules) | /api/events/stream (SSE), /api/agent/focus, /api/agent/reveal |
/findings | security/findings | /api/security, /api/finding-state, /api/finding-decision |
/policies (+detail) | security/controls, compliance/policies | /api/controls, /api/control-vocab, /api/control-test, /api/egress-decisions; staged localBlob('qdash.policies.v1') |
/compliance, /framework | compliance/frameworks | staged localBlob('qdash.compliance.frameworks'); findings all-time |
/posture | fleet/posture | /api/agents?period=all, /api/installations, /api/trends, /api/security |
/cost | fleet/costs | /api/costs (+ models, providers, accounts, workspaces, sessions for Cost Center, c105) |
/dlp, /identity, /performance, /campaigns | security/dlp, fleet/identity, fleet/performance, security/campaigns | /api/dlp, /api/inventory, /api/performance, /api/campaigns (GET/POST/DELETE) |
/alerts | alerts/alerts (+channels) | /api/security?period=all, /api/egress, /api/campaigns, /api/stats, /api/channels, /api/controls |
/integrations (+[id]) | alerts/integrations | /api/integrations (GET/POST/DELETE) |
/overview, /reports, /settings, /account | — | placeholders (DesignInProgress) |
Routes no UI module reads: /api/activity, /api/attestations. /api/security-packet is read by the Policies evidence packet.
3.3 Served, derived, staged (R23)
- Served: everything in §3.2's routes column, typed in
types/api/*.tswith the handler's field names. - Derived:
lib/*-rollups.tsand*-bands.ts(pure, specced): posture, findings (group, SLA, status), people, endpoints, harnesses, costs, team cost trend, alerts, compliance, index micros, surface, and the live view's virtual rows. - Staged state (R25,
local()/localBlob): framework activation and control toggles (compliance/frameworks.ts), policy enablement, exceptions, lists, detectors, thresholds (compliance/policies.ts). Each names the contract that retires it:GET /api/frameworks(+/frameworks/<slug>),GET /api/policies. - Staged values (R24,
lib/projected/*.ts, 66 ledger entries): alerts 19 (delivery, connector registry, secrets at rest, default routing, source heuristic), compliance 30 (catalogs, requirements, level counts, detector hits, quota thresholds, exception fixtures, framework links), fleet 7 (guardrail rule, enforcement, factor points, credential risk, label join), inventory 6 (tool success, provider labels, workspace git), people 4 (seats, team assignment), security 10 (SLA rubric, event log, operator assignee, expiry in note, DLP labels), session 4 (summary, provider of session). Every entry'scontractfield is a backend ask; §6 consolidates them.
4. The qdash backend: 38 /api/* routes (web.rs lines 59–132)
| Route | Serves | Window | Writes |
|---|---|---|---|
events/stream | SSE live tail of hub events, no replay or cursor; 503 on static serve | live | — |
agent/focus, agent/reveal | focus a harness window / reveal a file on this host (macOS bundles) | — | POST (host-local action) |
stats | fleet headline numbers, world seam, coverage {injection_failures, adapter_errors, uninspectable_conns} | cost and tokens 24h fixed | — |
inventory | per-run identity and credential rows (Identity page) | ?period= | — |
agents, agent | (harness × user × host) identities; detail by id | ?period= listing; detail all-time | — |
costs | spend and tokens by model / harness / user / team / provider + daily series; rows keyed by label | ?period=, ?team= | — |
performance | LLM latency per harness, twelve slowest | none | — |
egress | destination hosts with TLS and allow-list standing | none | — |
activity | event-family composition and per-second timelines (unused by the UI) | none | — |
trends | 90-day daily posture, spend, egress, findings, coverage | fixed 90 UTC days | — |
teams | email → team map (qdash-side; "an IdP/SCIM sync replaces the source later") | — | POST upsert |
dlp | sensitive file movement, secrets and PII by category | ?period=, ?team=, ?q=; volume unwindowed | — |
security | findings (control × scope) with workflow row; sla_due served, never written | ?period=, ?team= | — |
security-packet | evidence packet JSON | current state | — |
finding-state, finding-decision | one UPSERTed workflow row per finding key; apply a decision | — | POST |
controls, control-vocab, control-test | control catalog (cond, sev, act, enf, packs), vocabulary, dry-run | — | POST upsert, DELETE archive, POST test |
egress-decisions | operator decisions per (host × scope × agent) | — | POST upsert |
installations | at-rest scan snapshot, instrumented, fp_confirmed | none (inventory) | — |
endpoints | developer machines and what runs on them; ?host= detail | ?period= activity; installs at rest | — |
surface | what an actor touches, one axis per list | ?<scope>=&period= | — |
models, mcp-servers, workspaces, tools, sessions, harnesses, providers, accounts, skills | the inventory categories, list by default, detail by the category's identity param | ?period= (24h / 7d / 30d, else all; cut back from fleet_anchor, not the wall clock) | — |
attestations | evidenced out-of-band clauses (unused by the UI) | — | POST upsert |
channels | severity → connector routing register | — | POST upsert |
integrations | connector config, stored verbatim including secrets | — | POST upsert, DELETE |
campaigns | campaigns with live progress; wall-clock windows | — | POST create, DELETE |
SQLite behind every route (db.rs); period_span (web.rs:2334) accepts only 24h / 7d / 30d; no from / to; no auth; no org column.
5. What moves, what is replaced, what is dropped
- Moves (frame, re-homed on the new identity and API): sign-in / sign-up / sign-out / profile, SSO (SAML routes kept, OIDC added, D2), orgs and members with roles and permissions, invitations, the org switcher and user popover, settings (Organization, Team, SSO, Capabilities, Tokens / deployments with the first-ping wait, Snapshot), the onboarding checklist, the wizard kit and the Install flow (Choose environment → instructions → wait for the first ping → done), the Pulse plugin's one-client pattern (already the bible's R14),
useList/useEntity's protocol (already the primitives'). - Moves (pages, as they are): all 40 qdash pages with data, their shell, filter,
when:pill, honest states,types/api, rollups and specs. Transport swap only (R2). - Replaced: app.qpoint.io Inventory → qdash Inventory + Organization; Security overview and data-risk → Findings, Posture, DLP; app.qpoint.io alerting rules, events and webhooks → qdash Alerts + Integrations fed by a real engine (D7: the rule kinds threshold / delta / discover / risk and the webhook adapter are the spec of that engine); Firestore live subscriptions → polling (or an API stream) per R1; Cloud Functions (profile, invitations, permissions) → the Go app; contexts ("Views") → the global filter unless Tyler keeps member-scoped views.
- Dropped: Traffic (performance, availability, bandwidth, connections, requests), Stacks (adapters, rules, routing), the
$warehouseclient, GTM and Sentry wiring as built, the/devand/libshowcases,agent/focusandagent/revealon the plane (they belong to the native app, c131 / c137),DesignInProgressplaceholders, thedemo=truequery flow (c113's worlds replace it, D5).
6. The API gap list
What the qdash pages (and the frame) need that the 38 routes do not serve. Sources: types/api/* headers, lib/projected/*.ts contract fields, page comments, c128 data-inventory.md, c105, c131, c136, c137.
| # | Gap | Needed by | Recorded at |
|---|---|---|---|
| 1 | from / to on every windowed list and rollup (period_span takes three presets) | the when: pill's range picker, every report | c128 data-inventory.md gap 1; CYCLE.md:32 |
| 2 | One window on every route: stats (24h fixed), trends (90d), performance, egress, activity, installations, and the pages that fetch all-time (posture, compliance, alerts, identity, integrations, detail pages) | window consistency; reports vs pages | c128 gap 5; lib/projected/compliance.ts unwindowed; alerts.ts severityCountsAllTime |
| 3 | History beyond 90 days (/api/trends) | quarterly and SOC 2 reports | c128 gap 2 |
| 4 | Daily snapshot store: inventory counts, coverage, versions as of date X | reports "as of"; evolution views | c128 gap 3; c128 plan Phase 6 |
| 5 | Viewer identity GET /api/me (name, email, roles) and the authenticated operator on writes | /account; finding-decision assignee | pages/account.vue; projected/security.ts operatorAssignee |
| 6 | Sessions: local accounts, OIDC, SAML, sign-out, password reset, invitation acceptance | sign-in frame | app.qpoint.io /jwt/tokens, /sso/*, connectors; /forgot-password has no page |
| 7 | Orgs, members, roles and permissions, invitations (twelve permission strings today) | org frame, nav gating | composables/user/orgs.js, org/members.js, user/invitations.js |
| 8 | Contexts / views (member-scoped filter presets) or an explicit decision to drop them | org frame | app.qpoint.io orgs/{id}/contexts, Pulse /contexts |
| 9 | Settings: GET/POST /api/settings (org name, SSO config, hosting, deploy preferences, tags) | /settings | pages/settings.vue comment; app.qpoint.io settings/* |
| 10 | Endpoint connect: base URL + standardised credential exchange, enrolment, heartbeat | the native app's Connect; Install wizard's first ping | c131 plan D13; c134 D8; app.qpoint.io deployments.token/.active |
| 11 | Mechanic-access grants as plane objects (time-boxed, categorised) | c136 | c136 plan D1; c134 D8 |
| 12 | Operator inbox and send (messages as a resource) | c137 | c137 plan D1; c134 D8 |
| 13 | GET /api/frameworks (+/frameworks/<slug> requirements → control ids, activation state) | /compliance, /framework | compliance/frameworks.ts (R25); projected/compliance.ts gap 7 |
| 14 | GET /api/policies (enabled, config, exceptions, lists, detectors, thresholds) | /policies | compliance/policies.ts (R25, backlog B3) |
| 15 | Seat prices per provider plan; seats on /api/accounts | Cost Center, Users | c105 plan.md:55; projected/people.ts gap 10 |
| 16 | Identity roster (SSO / SCIM / HRIS) replacing the qdash-side /api/teams map | Users, Teams, findings by team | web.rs teams_list comment; projected/people.ts teamAssignment |
| 17 | An alert engine: rules on a schedule, an events history, delivery, enable / disable | /alerts, /integrations | projected/alerts.ts delivery, connectorCatalog; app.qpoint.io Pulse /alerts, /events/alerts |
| 18 | Connector registry with status, last_delivery_at, last_error; a redacting read path and secret store for /api/integrations | /integrations | projected/alerts.ts secretsAtRest; types/api/integration.ts (audit C) |
| 19 | Server-side default routing on /api/channels | /alerts | projected/alerts.ts defaultRoute |
| 20 | entity_id on /api/costs rows and /api/performance rows | drill-downs | types/api/cost.ts, performance.ts; projected/fleet.ts labelJoin |
| 21 | Labels: provider label on /api/models, vendor label and os on /api/agent, category labels on /api/dlp | Model, Harness install, DLP | projected/inventory.ts, security.ts (audit B) |
| 22 | git {remote, host, branch} on /api/workspaces | Workspaces | types/api/workspace.ts; projected/inventory.ts (gap 9) |
| 23 | Findings workflow: sla_due written, a finding_events log, expires_at, host on the row | Findings | types/api/finding.ts; projected/security.ts (gap 6) |
| 24 | Skill invocation as a session event; judged tool invocations (success rate); session summary; provider on session rows | Skills, Tools, Activity | web.rs skills comment; projected/inventory.ts toolSuccess; session.ts |
| 25 | credential_risk on /api/inventory; posture factors on /api/agents; per-level counts for policy levels | Identity, Posture, Policies | projected/fleet.ts, compliance.ts levelCounts |
| 26 | Live stream with a cursor or replay, from the plane's store; hub {connected, subscribers} on /api/stats | /live | types/api/live.ts; projected/alerts.ts liveInbound |
| 27 | Sensor (qcontrol / native app) version, heartbeat and health per endpoint; show Stats.coverage | Endpoints, reports, the self-hosted health page | c128 gap 4; data-inventory.md:95,103 |
| 28 | Deployment denominator (identity or MDM roster) | coverage % | c128 data-inventory.md (sensor-coverage-gap) |
| 29 | Org scoping on every call and every row (even at one org per install, D3) | SaaS later | c134 plan R1, D3 |
| 30 | Install status: first run, first admin, databases reachable, version, last update check | the install wizard; the health page | c134 plan deliverable 5, patterns |
Thirty gaps; the window (1–4) and identity (5–9) groups block the most pages.
7. Backend asks, consolidated
| Ask | Where it is already recorded | Status |
|---|---|---|
from / to on every page; one window everywhere | c128 CYCLE.md:32, data-inventory.md gaps 1 and 5, plan.md:187; c134 plan "What exists" | handed to Tyler 2026-11-03 |
| History beyond 90 days; snapshots | c128 gaps 2–3; plan.md Phase 6 | handed to Tyler |
| Sensor version, heartbeat, health | c128 gap 4; data-inventory.md:95,103 | handed to Tyler |
| Seat prices (served, customer-entered) | c105 plan.md:55 ("goes on the backend-asks list"); projected/people.ts gap 10 | open; c105 parked |
| Frameworks and policies served (R25 swaps) | compliance/frameworks.ts, policies.ts; projected/compliance.ts gap 7; bible backlog B3 | open since c99 |
Every contract in lib/projected/*.ts (66 entries) | the ledger itself (grep contract app/lib/projected) | open since c99 audits A–C |
/api/me, /api/settings | pages/account.vue, pages/settings.vue | placeholders since c103 |
| Connect credential, grants, messages | c131 D13, c136 D1, c137 D1; c134 D8 | ruled 2026-11-09, unspecified |
| IdP / SCIM identity source | web.rs teams_list doc comment | noted in code only |
| Alert engine and sender | projected/alerts.ts; app.qpoint.io composables/alerting/ as the spec | D7 |
8. For c135: what the web app needs that the layer (0.9.37) does not have
Start from ui/LAYER-CANDIDATES.md (17 staged components in ui/app/components/lib/: ProjectedBadge, ProjMark, ProjectedRing, SimulatedBadge, Panel, StatTile, Spark, MetricCell, BarList, AttentionStrip / Message / List / Reasons, JsonTree, AlertHandlers, SkeletonRows, StateEmpty; the composable useVirtualRows; and the state port for DataTable, UxTableList, BarList, DataMachine) plus the Opus track's 59 fixes (git show archive/qdash-opus-5.5:crates/qdash/apps/claude-code-opus-5.5/LAYER-CANDIDATES.md). Add, from app.qpoint.io's frame (all app-local there, none in q-nuxt-layer/components/):
| Need | Prior art | Layer today |
|---|---|---|
| Session card layout (logo over a white card) | layouts/session.vue | none (layout/Grid only) |
| OAuth / SSO button with provider mark | components/ux/OauthBtn.vue | none; UxButton has no provider variant |
| Sign-in / sign-up form with the SSO switch | pages/sign-in.vue + useSSO | UxInput, UxButton, form/* exist; the composition does not |
| Org switcher | pages/orgs/components/Selector.vue (headlessui Listbox) | UxSelect, UxPopover; no switcher |
| View / context selector (if views survive) | orgs/components/contexts/Selector.vue | none |
| User menu popover (avatar → Account, Sign out) | layouts/components/nav/User.vue, page/AccountMenu.vue | UxAvatar, UxPopover; no menu |
| Invite member form with a role picker | settings/team/manage.vue, team/NewMember.vue | UxCheckbox, form/SaveBar; no role picker |
| SSO provider form (OIDC / SAML) | settings/sso.vue, sso/components/provider/* | none |
| Secret / certificate box (copy, view, edit) | ux/CertBox.vue, ux/CopyBox.vue | UxCopyBtn, UxCode; no boxed secret |
Wizard kit: Steps, EduHarness, PingListener (+radar), ToggleHarness, FinalStep, Sequin | components/wizard/; design app/components/replicas/wizard/ (c125) | prose/Step is a doc step, not a wizard |
| Onboarding checklist (task master + summary) | components/task/Master.vue, Summary.vue | none |
| Toast stack + backend banner (R31 shell parts) | qdash components/shell/ToastStack.vue, BackendBanner.vue | none |
Date-range picker for the when: pill (from / to) | c128 (ruled: the pill's panel) | Filter has presets only |
| Export helper (CSV / JSON, print styles) | qdash lib/download.ts; c128 | none |
| Code block with title, height and copy (the Snapshot page) | ux/CodeHighlighter.vue | UxCodeBlock: verify parity |
| Flash / incoming-deploy message strip | page/Messages.vue, stores/flash.ts | none |
Not needed if D1 holds: app.qpoint.io's PageMain / TopNav / teleported filter slot (qdash's shell is the frame's body), ux/Teleport, the traffic cells (HttpStatus, RequestMethod, table-list/td/*).
9. Open questions for Tyler and Kamal
- Is Pulse's source readable by design? Which of its patterns survive: the server-side filter grammar,
/contexts(member-scoped views), the alert rule kinds? - Identity: OIDC plus local accounts for self-hosted, SAML kept (D2). Who issues the session, and is it a cookie or a bearer the UI holds? Where do invitations live?
- Tenancy: one org per install (D3). Does the schema carry
org_idon every row from day one so SaaS multi-org is a flag, not a migration? - The window:
from/toon every list and rollup, withperiodkept as presets; cut back from the wall clock or from the data's anchor (the baked worlds depend on the anchor)? - Alerting (D7): does the Go app run the rule engine and the senders, with app.qpoint.io's four rule kinds and webhook adapter as the first spec, or is alerting a later phase?
- The live tail: a stream from the Go app over ClickHouse with a cursor, or polling only at first?
- Endpoint connect (c131 D13): credential format, enrolment, heartbeat cadence; does the heartbeat carry sensor version and health (gap 27)?
- Contract generation: TypeScript (and Rust for the native app) from the Go handlers or a shared schema, per c131 R3?
demo.qpoint.io: do c113's baked worlds survive the new routes (they are keyed by URL), or is the demo re-baked against the new API?- Host-local actions (
agent/focus,agent/reveal): confirm they leave the plane for the native app. - The qproxy settings (proxy, TLS, stacks, qtap filters): dropped, or does qproxy return under the plane?