Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -370,11 +370,13 @@ outcome. For SSE the response-created event marks
successful dispatch, not stream termination; close/cancel/error observability remains part of the
browser/realtime lifecycle slice. Client cancellation is informational and carries neither a
failure fingerprint nor a server-error outcome. Bun's outer 640 KiB pre-dispatch body ceiling
supports the largest reviewed task-content request and may reject before application correlation
exists. The raw tRPC boundary selects exact registered-procedure ceilings before parsing or
authentication: 16 KiB for authentication, 32 KiB for WebAuthn, 64 KiB by default, 128 KiB for
task progress, and 640 KiB for task create/content update. Unknown procedures retain the default
ceiling, while unknown authentication-namespace procedures retain the stricter authentication
supports the largest reviewed task-content and monitoring mutation requests and may reject before
application correlation exists. The raw tRPC boundary selects exact registered-procedure ceilings
before parsing or authentication: 16 KiB for authentication, 32 KiB for WebAuthn, 64 KiB by
default, 128 KiB for task progress, and 640 KiB for task create/content update, complete monitoring
snapshots, and
immutable report upserts. Unknown procedures retain the default ceiling, while unknown
authentication-namespace procedures retain the stricter authentication
profile. The raw handler receives the generated ID and resolves direct-client provenance against
the exact trusted-proxy allowlist before context construction. `createContext` then authenticates the
already parsed session or automation credential and establishes identity plus audit correlation
Expand All @@ -387,7 +389,7 @@ once. Reusable procedure builders are limited to:

Expected errors use a small stable code set such as `UNAUTHORIZED`, `FORBIDDEN`, `CONFLICT`,
`NOT_FOUND`, `PRECONDITION_FAILED`, `TOO_MANY_REQUESTS`, and `SERVICE_UNAVAILABLE` with safe
structured details. The `ContractErrorCode` union, all 55 actual router paths, the server-owned
structured details. The `ContractErrorCode` union, all 68 actual router paths, the server-owned
runtime allowlist, and generated contract metadata must match exactly. The base procedure
middleware enforces that allowlist for immediate and deferred subscription failures; an
implemented procedure missing from the policy or an undeclared code becomes a redacted internal
Expand Down
29 changes: 28 additions & 1 deletion greenfield/docs/architecture/greenfield-rewrite/progress.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ closes a phase; dated entries below provide the evidence, not a second status so
| 0 — Evidence and qualification | Complete | All eight mandatory spikes pass on exact Bun revision `17d6843606d76620cb55d31424d7fb0aed51c367`: build, transport, cross-process SQLite/outbox, Drizzle/Bun SQLite, browser data, chat batching, shutdown, and capped resources. Source-derived parity and the OpenClaw source audit pass as additional evidence. |
| 1 — Foundation | Complete | The self-contained future root builds immutable browser/web/worker artifacts, protects project-local production state, installs exact Bun and systemd artifacts, migrates a database copy, atomically promotes the release/database pair, serves readiness/browser assets, writes project-local logs, and proves crash-safe rollback and shutdown in a disposable lifecycle. |
| 2 — Trust and transport | Complete for the stated server scope | Authentication, MFA, WebAuthn, automation credentials, audit, authenticated renewable SSE, one-shot native Gateway bootstrap verification, and the consolidated [threat model](../../security/greenfield-phase-two-threat-model.md) have executable evidence. Browser UI and production cutover remain later gates. |
| 3 — Core operator domains | Started | Task and agent-directory parity are implemented with durable history, realtime invalidation, and browser workflows. Report, incident, notification, schedule/job, cache/metrics, overview, and worker-domain parity remain open. |
| 3 — Core operator domains | Started | Task and agent-directory parity are implemented with durable history, realtime invalidation, and browser workflows. Monitoring ingestion plus report, incident, and notification server parity are implemented. Their browser workflows, schedules/jobs, overview, cache/metrics, and the real worker remain open. |
| 4 — Gateway and chat | Not started | The Phase 2 verifier is one-shot only. Persistent native Gateway lifecycle, current-protocol re-audit, sessions, chat journal/recovery, attachments, and frontend remain open. |
| 5 — Privileged and external domains | Not started | Worker-owned file/media, Docker, database, OpenClaw, GitHub, deployment, backup, and other privileged adapters remain open. |
| 6 — Parity, hardening, and cutover | Not started | Full UI parity, generated `/docs`, load/resource/restore evidence, cutover rehearsal, fresh production database, and legacy removal remain open. |
Expand Down Expand Up @@ -841,3 +841,30 @@ full-browser parity, production rehearsal, cutover, and legacy deletion remain o
Persistent OpenClaw/Gateway availability and session state remain Phase 4 work; reports,
incidents, notifications, schedules/jobs, overview, cache/metrics, and the real worker remain
open Phase 3 gates.

### 2026-08-07 — Phase 3 monitoring ingestion and catalog slice

- One automation-only `monitoring.submitCompleteSnapshot` boundary now exposes the complete-run
state machine through `monitoring:write`. It shares the production database repository,
immediate-write admission, domain clock, and post-commit realtime wake path with the report,
incident, and notification catalogs.
- Twelve catalog procedures provide keyset-paginated incident, notification, and report reads;
immutable report upserts for scoped browser sessions or automation callers; automation-only
notification producer upserts; session-owned notification read/delete actions; and bounded
report deletion. Exact `reports:*` and `notifications:*` capabilities and principal-kind
middleware keep browser-session actions separate from automation-only ingestion.
- Complete snapshots and report upserts have a qualified 640 KiB transport profile plus a stricter
512 KiB semantic aggregate budget. Each embedded JSON object retains its separate 64 KiB budget.
Exact registered procedure matching prevents unknown, malformed, or batched names from inheriting
the larger allowance, while authentication and WebAuthn retain their stricter profiles.
- Typed validation, replay, catalog conflict, not-found, precondition, and database-admission
failures map to the declared tRPC error policy. Mutations clamp durable timestamps across clock
regressions, cap report-linked deletion work, and keep catalog writes, audit state, and compact
realtime invalidations atomic.
- Generated procedure and realtime documentation now comes from the same Valibot contracts. The
reviewed parity inventory marks all six notification and four report legacy operations
implemented; incident reads and complete monitoring ingestion are net-new. Incident-generation
notifications retain a forward deep link to the immediately stacked authenticated `/incidents`
reader rather than choosing one arbitrary observation report; greenfield stays inactive until
the complete cutover stack lands. Browser workflows, schedules/jobs, overview, cache/metrics,
and real worker execution remain open Phase 3 gates.
13 changes: 13 additions & 0 deletions greenfield/docs/generated/procedures.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@
| `automationSecurity.revokeCredential` | mutation | automation-security | MFA enrollment required; recent MFA when enabled | [input](./schemas/automationSecurity.revokeCredential.input.schema.json) | [output](./schemas/automationSecurity.revokeCredential.output.schema.json) | `CONFLICT`, `FORBIDDEN`, `NOT_FOUND`, `SERVICE_UNAVAILABLE`, `UNAUTHORIZED` | `mfa_enrollment_required`, `step_up_required` | Explicitly revokes one automation credential after client cutover. |
| `automationSecurity.rotateCredential` | mutation | automation-security | MFA enrollment required; recent MFA when enabled | [input](./schemas/automationSecurity.rotateCredential.input.schema.json) | [output](./schemas/automationSecurity.rotateCredential.output.schema.json) | `CONFLICT`, `FORBIDDEN`, `NOT_FOUND`, `PRECONDITION_FAILED`, `SERVICE_UNAVAILABLE`, `UNAUTHORIZED` | `mfa_enrollment_required`, `step_up_required` | Stages a linked replacement credential without revoking its predecessor. |
| `events.stream` | subscription | events | Authenticated; per-topic: agents:read, notifications:read, reports:read, tasks:read | [input](./schemas/events.stream.input.schema.json) | [output](./schemas/events.stream.output.schema.json) | `BAD_REQUEST`, `FORBIDDEN`, `SERVICE_UNAVAILABLE`, `TOO_MANY_REQUESTS`, `UNAUTHORIZED` | None | Streams authorized durable changes with tracked resume cursors. |
| `incidents.get` | query | incidents | Authenticated: reports:read | [input](./schemas/incidents.get.input.schema.json) | [output](./schemas/incidents.get.output.schema.json) | `FORBIDDEN`, `NOT_FOUND`, `UNAUTHORIZED` | None | Loads one exact incident lifecycle record. |
| `incidents.list` | query | incidents | Authenticated: reports:read | [input](./schemas/incidents.list.input.schema.json) | [output](./schemas/incidents.list.output.schema.json) | `FORBIDDEN`, `UNAUTHORIZED` | None | Lists stable incident lifecycle rows for report navigation. |
| `monitoring.submitCompleteSnapshot` | mutation | monitoring | Authenticated automation principal: monitoring:write | [input](./schemas/monitoring.submitCompleteSnapshot.input.schema.json) | [output](./schemas/monitoring.submitCompleteSnapshot.output.schema.json) | `BAD_REQUEST`, `CONFLICT`, `FORBIDDEN`, `SERVICE_UNAVAILABLE`, `UNAUTHORIZED` | None | Atomically ingests one complete monitor snapshot. |
| `notifications.clearRead` | mutation | notifications | Authenticated browser session: notifications:write | [input](./schemas/notifications.clearRead.input.schema.json) | [output](./schemas/notifications.clearRead.output.schema.json) | `FORBIDDEN`, `SERVICE_UNAVAILABLE`, `UNAUTHORIZED` | None | Deletes one bounded page of matching read notifications. |
| `notifications.delete` | mutation | notifications | Authenticated browser session: notifications:write | [input](./schemas/notifications.delete.input.schema.json) | [output](./schemas/notifications.delete.output.schema.json) | `FORBIDDEN`, `NOT_FOUND`, `SERVICE_UNAVAILABLE`, `UNAUTHORIZED` | None | Deletes one exact Dashboard notification. |
| `notifications.list` | query | notifications | Authenticated: notifications:read | [input](./schemas/notifications.list.input.schema.json) | [output](./schemas/notifications.list.output.schema.json) | `FORBIDDEN`, `UNAUTHORIZED` | None | Lists a stable filtered notification page and unread count. |
| `notifications.markAllRead` | mutation | notifications | Authenticated browser session: notifications:write | [input](./schemas/notifications.markAllRead.input.schema.json) | [output](./schemas/notifications.markAllRead.output.schema.json) | `FORBIDDEN`, `SERVICE_UNAVAILABLE`, `UNAUTHORIZED` | None | Marks one bounded page of matching notifications read. |
| `notifications.markRead` | mutation | notifications | Authenticated browser session: notifications:write | [input](./schemas/notifications.markRead.input.schema.json) | [output](./schemas/notifications.markRead.output.schema.json) | `FORBIDDEN`, `NOT_FOUND`, `SERVICE_UNAVAILABLE`, `UNAUTHORIZED` | None | Marks one notification read idempotently. |
| `notifications.upsert` | mutation | notifications | Authenticated automation principal: notifications:write | [input](./schemas/notifications.upsert.input.schema.json) | [output](./schemas/notifications.upsert.output.schema.json) | `BAD_REQUEST`, `CONFLICT`, `FORBIDDEN`, `NOT_FOUND`, `SERVICE_UNAVAILABLE`, `UNAUTHORIZED` | None | Creates a notification or accepts an exact idempotent replay. |
| `reports.delete` | mutation | reports | Authenticated: reports:write | [input](./schemas/reports.delete.input.schema.json) | [output](./schemas/reports.delete.output.schema.json) | `FORBIDDEN`, `NOT_FOUND`, `PRECONDITION_FAILED`, `SERVICE_UNAVAILABLE`, `UNAUTHORIZED` | None | Deletes one report while preserving its monitor-run history. |
| `reports.get` | query | reports | Authenticated: reports:read | [input](./schemas/reports.get.input.schema.json) | [output](./schemas/reports.get.output.schema.json) | `FORBIDDEN`, `NOT_FOUND`, `UNAUTHORIZED` | None | Loads one complete immutable Markdown report. |
| `reports.list` | query | reports | Authenticated: reports:read | [input](./schemas/reports.list.input.schema.json) | [output](./schemas/reports.list.output.schema.json) | `FORBIDDEN`, `UNAUTHORIZED` | None | Lists a stable filtered page of immutable report summaries. |
| `reports.upsert` | mutation | reports | Authenticated: reports:write | [input](./schemas/reports.upsert.input.schema.json) | [output](./schemas/reports.upsert.output.schema.json) | `BAD_REQUEST`, `CONFLICT`, `FORBIDDEN`, `SERVICE_UNAVAILABLE`, `UNAUTHORIZED` | None | Creates a report or accepts an exact idempotent replay. |
| `securityAudit.listEvents` | query | securityAudit | Authenticated browser session | [input](./schemas/securityAudit.listEvents.input.schema.json) | [output](./schemas/securityAudit.listEvents.output.schema.json) | `FORBIDDEN`, `UNAUTHORIZED` | None | Lists redacted immutable security events in stable newest-first order. |
| `system.runtimeIdentity` | query | system | Public | [input](./schemas/system.runtimeIdentity.input.schema.json) | [output](./schemas/system.runtimeIdentity.output.schema.json) | None | None | Returns the Bun runtime identity of the serving process. |
| `tasks.addUpdate` | mutation | tasks | Authenticated: tasks:write | [input](./schemas/tasks.addUpdate.input.schema.json) | [output](./schemas/tasks.addUpdate.output.schema.json) | `CONFLICT`, `FORBIDDEN`, `NOT_FOUND`, `SERVICE_UNAVAILABLE`, `UNAUTHORIZED` | None | Appends one authenticated progress update to a task. |
Expand Down
8 changes: 7 additions & 1 deletion greenfield/docs/generated/realtime-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@

> Generated by `bun run docs:generate`. Do not edit manually.

No standalone realtime topic references are published until their snapshot procedures are implemented.
| Topic | Payload | Snapshot | Retention | Summary |
| --- | --- | --- | --- | --- |
| `agents.status` | [payload](./schemas/agents.status.realtime.payload.schema.json) | `agents.listStatuses` | 7 days | Invalidates one agent status row after a durable metadata change. |
| `monitoring.incidents` | [payload](./schemas/monitoring.incidents.realtime.payload.schema.json) | `incidents.list` | 7 days | Invalidates incident lifecycle rows after a complete monitor snapshot. |
| `monitoring.notifications` | [payload](./schemas/monitoring.notifications.realtime.payload.schema.json) | `notifications.list` | 7 days | Invalidates Dashboard notifications after catalog changes. |
| `monitoring.reports` | [payload](./schemas/monitoring.reports.realtime.payload.schema.json) | `reports.list` | 7 days | Invalidates immutable reports after catalog changes. |
| `tasks.records` | [payload](./schemas/tasks.records.realtime.payload.schema.json) | `tasks.list` | 7 days | Invalidates one task row after a durable task-domain change. |
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$id": "urn:mira-dashboard:agents.status.realtime.payload",
"type": "object",
"properties": {
"id": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"pattern": "^[a-z0-9][a-z0-9._-]*$"
}
},
"required": [
"id"
],
"additionalProperties": false,
"$schema": "https://json-schema.org/draft/2020-12/schema"
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@
"enum": [
"agents:read",
"agents:write",
"monitoring:write",
"notifications:read",
"notifications:write",
"reports:read",
"reports:write",
"tasks:read",
"tasks:write"
],
"type": "string"
},
"maxItems": 6,
"maxItems": 9,
"uniqueItems": true
},
"id": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,17 @@
"enum": [
"agents:read",
"agents:write",
"monitoring:write",
"notifications:read",
"notifications:write",
"reports:read",
"reports:write",
"tasks:read",
"tasks:write"
],
"type": "string"
},
"maxItems": 6,
"maxItems": 9,
"uniqueItems": true
},
"createdAtMs": {
Expand Down Expand Up @@ -150,14 +153,17 @@
"enum": [
"agents:read",
"agents:write",
"monitoring:write",
"notifications:read",
"notifications:write",
"reports:read",
"reports:write",
"tasks:read",
"tasks:write"
],
"type": "string"
},
"maxItems": 6,
"maxItems": 9,
"uniqueItems": true
},
"createdAtMs": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,17 @@
"enum": [
"agents:read",
"agents:write",
"monitoring:write",
"notifications:read",
"notifications:write",
"reports:read",
"reports:write",
"tasks:read",
"tasks:write"
],
"type": "string"
},
"maxItems": 6,
"maxItems": 9,
"uniqueItems": true
},
"createdAtMs": {
Expand Down Expand Up @@ -99,14 +102,17 @@
"enum": [
"agents:read",
"agents:write",
"monitoring:write",
"notifications:read",
"notifications:write",
"reports:read",
"reports:write",
"tasks:read",
"tasks:write"
],
"type": "string"
},
"maxItems": 6,
"maxItems": 9,
"uniqueItems": true
},
"createdAtMs": {
Expand Down
Loading