diff --git a/AGENTS.md b/AGENTS.md index a2588e9..0caecc6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -40,4 +40,4 @@ Every production feature requires tests and every architecture change requires a **Permanent — no ADR lifts these.** Never hard-code secrets. Never change a contract without consumer analysis. Never put detailed metrics on-chain. Never put tenant payloads, logs, secrets, or any personal data on-chain: consensus state cannot be erased, so only hashes and commitments may cross that line (ADR-012 §3). -**Prohibited until the ADR gate named in ADR-012 §6 is accepted.** Do not introduce another database (ADR-013, ADR-018), direct Agent-to-chain access (ADR-017), runtime orchestration (ADR-016), decentralized storage (ADR-018), a TEE trust root (ADR-019), or a replacement for `EnsureRoot` governance (ADR-020). Kubernetes remains prohibited under ADR-006, which fixes Docker as the runtime; adopting it needs its own accepted ADR. +**Prohibited until the ADR gate named in ADR-012 §6 is accepted.** Do not introduce another database (ADR-016, ADR-021), direct Agent-to-chain access (ADR-020), runtime orchestration (ADR-019), decentralized storage (ADR-021), a TEE trust root (ADR-022), or a replacement for `EnsureRoot` governance (ADR-023). Kubernetes remains prohibited under ADR-006, which fixes Docker as the runtime; adopting it needs its own accepted ADR. diff --git a/ROADMAP.md b/ROADMAP.md index 947a9ac..c7763c7 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -52,19 +52,19 @@ Progressively remove centralized frontend, database, scheduler, and operational | Issue | Gate | |---|---| | #32 — decentralization roadmap and trust boundaries | ADR-012 (accepted) | -| #33 — replicated off-chain data plane | ADR-013 | -| #34 — multiple Control Planes and scheduling relays | ADR-014 | -| #35 — content-addressed frontend distribution | ADR-018 | -| #36 — decentralized identity, governance, validator operations | ADR-020 | -| #50 — orchestration in smart contracts | ADR-016 | +| #33 — replicated off-chain data plane | ADR-016 | +| #34 — multiple Control Planes and scheduling relays | ADR-017 | +| #35 — content-addressed frontend distribution | ADR-021 | +| #36 — decentralized identity, governance, validator operations | ADR-023 | +| #50 — orchestration in smart contracts | ADR-019 | | #51 — streaming payments | #19 (v1.1), no new ADR | -| #52 — slashing for availability guarantees | ADR-015 | +| #52 — slashing for availability guarantees | ADR-018 | ## v4.0 — P2P Mesh & Global Fabric Full P2P WireGuard mesh, DHT discovery, Virtual VPC and decentralized ingress/DNS. Stage 2 of ADR-012 §5: remove the Control Plane from the packet path. Depends on #36, because peer authentication cannot rest on a Control-Plane-issued allowlist once the Control Plane is no longer in the path. -Issues #53 (P2P mesh), #54 (gateway nodes), #55 (decentralized DNS) — all gated by ADR-017. +Issues #53 (P2P mesh), #54 (gateway nodes), #55 (decentralized DNS) — all gated by ADR-020. ## v5.0 — Geo-Distributed Economy & Storage @@ -72,10 +72,10 @@ Geo-discovery, Proof of Resource (PoR), and decentralized S3/block storage. Stag | Issue | Gate | |---|---| -| #56 — DHT geo-discovery | ADR-017 | +| #56 — DHT geo-discovery | ADR-020 | | #57 — Proof of Resource | none; extends ADR-007 and ADR-011 §3 | -| #58 — S3-compatible object storage | ADR-018 | -| #59 — replicated block volumes | ADR-018 | +| #58 — S3-compatible object storage | ADR-021 | +| #59 — replicated block volumes | ADR-021 | ## v6.0 — Confidential Cloud & Auto-Healing @@ -83,7 +83,7 @@ TEE support, distributed attestation, auto-migration and P2P IaC. Stage 4 of ADR | Issue | Gate | |---|---| -| #60 — TEE support (Intel SGX / AMD SEV) | ADR-019 | -| #61 — distributed enclave attestation | ADR-019 | -| #62 — auto-healing and workload migration | ADR-016 | -| #63 — infrastructure topology DSL | none while evaluated off-chain; ADR-016 if evaluated on-chain | +| #60 — TEE support (Intel SGX / AMD SEV) | ADR-022 | +| #61 — distributed enclave attestation | ADR-022 | +| #62 — auto-healing and workload migration | ADR-019 | +| #63 — infrastructure topology DSL | none while evaluated off-chain; ADR-019 if evaluated on-chain | diff --git a/docs/adr/012-decentralization-roadmap-and-trust-boundaries.md b/docs/adr/012-decentralization-roadmap-and-trust-boundaries.md index 78afb01..8e241d5 100644 --- a/docs/adr/012-decentralization-roadmap-and-trust-boundaries.md +++ b/docs/adr/012-decentralization-roadmap-and-trust-boundaries.md @@ -205,14 +205,14 @@ named in the last column. | Gate | Unblocks | Prohibition it must lift, and what it must settle | |---|---|---| -| **ADR-013** — replicated off-chain data plane | #33 | "another database". Must settle: event log vs CRDT, deterministic IDs, ordering, snapshots, pruning, and the PostgreSQL deprecation criteria | -| **ADR-014** — multi-Control-Plane and relay protocol | #34 | single-Control-Plane component boundary. Must settle: leader/leaderless rules, idempotency, peer admission, and how an Agent refuses an unauthenticated relay | -| **ADR-015** — slashing and economic penalties | #52 | none (new mechanism). Already demanded by ADR-011 §5, which ships rewards but explicitly defers slashing economics. Must settle: false-positive protection, appeals, and interaction with `dispute_round` | -| **ADR-016** — on-chain orchestration | #50, #62 | "runtime orchestration". Must settle: what scheduling logic is deterministic enough for the runtime, and what stays off-chain | -| **ADR-017** — P2P mesh and DHT discovery | #53, #54, #55, #56 | "direct Agent-to-chain access", and the Control-Plane-mediated key exchange in `control-plane/internal/wireguard/wireguard.go`. Must settle: peer authentication without a central introducer, and what stays lease-gated per ADR-010 | -| **ADR-018** — content-addressed distribution and decentralized storage | #35, #58, #59 | "another database". Must settle: pinning, retention proofs, erasure, and gateway trust | -| **ADR-019** — TEE and distributed attestation | #60, #61 | none (new trust root). Must settle: which vendor roots are trusted, revocation, and what an unattested provider may still do | -| **ADR-020** — decentralized identity, key rotation, and governance | #36 | `EnsureRoot` as governance (`blockchain/runtime/src/lib.rs:316`). Must settle: rotation and recovery per role, stake/delegation, timelocks, and emergency constraints | +| **ADR-016** — replicated off-chain data plane | #33 | "another database". Must settle: event log vs CRDT, deterministic IDs, ordering, snapshots, pruning, and the PostgreSQL deprecation criteria | +| **ADR-017** — multi-Control-Plane and relay protocol | #34 | single-Control-Plane component boundary. Must settle: leader/leaderless rules, idempotency, peer admission, and how an Agent refuses an unauthenticated relay | +| **ADR-018** — slashing and economic penalties | #52 | none (new mechanism). Already demanded by ADR-011 §5, which ships rewards but explicitly defers slashing economics. Must settle: false-positive protection, appeals, and interaction with `dispute_round` | +| **ADR-019** — on-chain orchestration | #50, #62 | "runtime orchestration". Must settle: what scheduling logic is deterministic enough for the runtime, and what stays off-chain | +| **ADR-020** — P2P mesh and DHT discovery | #53, #54, #55, #56 | "direct Agent-to-chain access", and the Control-Plane-mediated key exchange in `control-plane/internal/wireguard/wireguard.go`. Must settle: peer authentication without a central introducer, and what stays lease-gated per ADR-010 | +| **ADR-021** — content-addressed distribution and decentralized storage | #35, #58, #59 | "another database". Must settle: pinning, retention proofs, erasure, and gateway trust | +| **ADR-022** — TEE and distributed attestation | #60, #61 | none (new trust root). Must settle: which vendor roots are trusted, revocation, and what an unattested provider may still do | +| **ADR-023** — decentralized identity, key rotation, and governance | #36 | `EnsureRoot` as governance (`blockchain/runtime/src/lib.rs:316`). Must settle: rotation and recovery per role, stake/delegation, timelocks, and emergency constraints | Three issues need **no new gate**: @@ -223,7 +223,7 @@ Three issues need **no new gate**: - **#51 streaming payments** is gated on the metering and settlement architecture already scoped by #19 (milestone v1.1), not on a new ADR. - **#63 IaC DSL** needs no gate while it is evaluated off-chain. If any part of it - is ever evaluated in the runtime, it falls under ADR-016 and inherits the + is ever evaluated in the runtime, it falls under ADR-019 and inherits the determinism, bounded-input, and no-floats rules of `AGENTS.md:23`. ### 7. Trade-offs and rollback @@ -280,3 +280,20 @@ It does not change any code, origin, or storage item today. (`009-control-plane-provider-registration.md` and `009-local-aura-grandpa-testnet.md`). This ADR takes `012` and does not renumber them; the collision is noted so it is not repeated. +- **§6's gate numbers were renumbered once, after the fact.** This ADR originally + reserved `ADR-013` through `ADR-020` for the eight gates above. `ADR-013` and + `ADR-014` were then accepted for unrelated, unplanned work (the Network + Validator daemon and wallet-based dashboard login, respectively — neither is a + Stage 1+ decentralization gate), and `ADR-015` was about to be claimed the same + way (independent bandwidth measurement) before this correction. Rather than let + `AGENTS.md`'s prohibition list keep citing gate numbers that actually name + something else, every gate in §6 (and every citation of it in `AGENTS.md` and + `ROADMAP.md`) was shifted by three: `ADR-013`→`ADR-016`, + `ADR-014`→`ADR-017`, `ADR-015`→`ADR-018`, `ADR-016`→`ADR-019`, + `ADR-017`→`ADR-020`, `ADR-018`→`ADR-021`, `ADR-019`→`ADR-022`, + `ADR-020`→`ADR-023`. `ADR-015` (bandwidth measurement) still lands as the real, + accepted `ADR-015` — a coincidence of timing with this correction, not a + renumbering of it. Future ADR numbers are assigned strictly in submission order + from whatever is next free at the time; this table is a *reservation of + intent*, not a claim on the number, and the next accepted ADR of any kind + (gate or not) takes the next integer regardless of what this table says. diff --git a/docs/control-plane/dashboard-decentralized-hosting-migration.md b/docs/control-plane/dashboard-decentralized-hosting-migration.md new file mode 100644 index 0000000..003bdb2 --- /dev/null +++ b/docs/control-plane/dashboard-decentralized-hosting-migration.md @@ -0,0 +1,117 @@ +# Dashboard: migration path to decentralized static hosting + +## Status + +Planning note, not an ADR. It authorizes no implementation by itself — see "Relationship to +ADR-012" below. Written to close the corresponding item in issue #76 ("a migration path from the +MVP same-origin UI to decentralized static hosting — not written up anywhere yet"). + +## Where the dashboard is today (Stage 0) + +`control-plane/internal/dashboard` serves the dashboard as static assets embedded directly in the +Control Plane's own Go binary (`//go:embed assets/*`, `internal/dashboard/dashboard.go`) at +`GET /dashboard/`, same-origin with every JSON API it calls (`/api/v1/overview`, +`/api/v1/validator-scores/{provider_id}`, the `/api/v1/auth/*` wallet-login endpoints). This is +exactly ADR-012 §3's Stage 0 placement for the "Dashboard / frontend assets" data class: "Served by +the Control Plane". It is also a single point of failure and a single point of trust — one +operator's binary is both the only place the UI can be fetched from and the only origin the +browser's `Content-Security-Policy` (`default-src 'self'`, `internal/dashboard/dashboard.go`'s +`securityHeaders`) will trust. + +ADR-012 §3 already names the target: **content-addressed**, gated behind the still-unwritten +"content-addressed distribution and decentralized storage" ADR (renumbered `ADR-021` per ADR-012's +own "Consequences" correction — see that ADR for why the number moved). That gate ADR is scoped +far beyond the dashboard alone: it also covers S3-compatible object storage (#58) and replicated +block volumes (#59), and per ADR-012 §6 it "must settle: pinning, retention proofs, erasure, and +gateway trust" for *all three*. This document does not attempt to settle those — it only lays out +the dashboard-specific path so that work has somewhere to land once the gate ADR exists, and so +`#76`'s acceptance criterion has an honest answer today rather than silence. + +## Why this is harder than "upload the build to IPFS" + +The dashboard is not a static site today — it is a thin client over authenticated, live, +same-origin APIs: + +- **Same-origin CSP.** `securityHeaders` sets `connect-src 'self'`. A dashboard served from a + content-addressed host (a different origin than the Control Plane API) needs a CSP that names the + API origin(s) explicitly, which reopens a CORS/CSRF design question this repository has not + needed to answer yet — today "same origin" *is* the authentication boundary for the unauthenticated + read endpoints (`agentEndpoint`, `validatorScores`), and loosening it changes that boundary. +- **Wallet login's origin binding.** ADR-014's challenge/signature flow (`internal/walletlogin`) and + its browser client (`assets/auth.js`) generate and store an Ed25519 keypair in the browser's + `localStorage`, which is origin-scoped by the browser itself. Moving the UI to a + content-addressed origin either fragments a user's stored key per hosting mirror (bad UX: "log + in again from every gateway") or requires a deliberate decision to widen key storage beyond + single-origin `localStorage` (a real security-relevant change, not a hosting detail). +- **Multiple Control Plane replicas don't exist yet.** ADR-012 Stage 1 (#34, gated by the + renumbered `ADR-017`) is what makes "which Control Plane API does this dashboard build talk to" + a real question with more than one honest answer. Decentralizing the frontend before + decentralizing the API it calls only decentralizes the part that was never the trust bottleneck. +- **Versioning and rollback.** A content-addressed build is immutable by construction (new build, + new hash). ADR-012 §7's rollback guarantee ("every stage must keep its predecessor operable for + one release") means an old, pinned build must keep working against a newer Control Plane API for + at least one release window — this repository's dashboard/API pairing has never had to be + backward compatible before, because there has only ever been one build talking to one API. + +None of this is a reason not to do it. It is the reason it needs the gate ADR's actual +pinning/retention/erasure/gateway-trust decisions settled first, rather than a build script bolted +on ahead of them. + +## Proposed staged path + +Mirrors ADR-012 §5's own staging discipline: each stage removes one piece of the current +same-origin assumption, keeps the previous stage's path working, and is explicit about what +degrades if it fails. + +**Stage 0 (today).** Embedded in the Control Plane binary, same-origin. No action needed; this +document changes nothing about it. + +**Stage 0.5 — decoupled build, still Control-Plane-served (no new trust boundary).** Extract +`internal/dashboard/assets` into a standalone static build (its own `package.json`/build step is +optional — the assets are already hand-written HTML/CSS/vanilla JS with no bundler dependency +today, so "build" may just mean "lint and version-stamp"), still served by +`//go:embed` from the same binary at the same origin. This is pure refactoring with zero behavior +change and needs no ADR: it does not move any data class, per ADR-012 §3's "Nothing here moves a +class from off-chain to on-chain" framing (moving *how a build is produced* is not moving *where a +data class lives*). Value: makes every later stage a smaller diff, and lets CI verify the dashboard +build in isolation from the Go binary. + +**Stage 1 — content-addressed mirror, Control-Plane-served remains the default and the source of +truth.** Publish each build's static output to content-addressed storage (IPFS or equivalent — the +gate ADR chooses) *in addition to* the existing `//go:embed` path, with the Control Plane +continuing to serve `/dashboard/` itself as the default entry point. The published hash is +advertised (e.g. a `GET /api/v1/dashboard-build` endpoint reporting the current build's hash), +letting an operator who wants to self-host or mirror it not depend on this Control Plane's uptime, +without yet requiring any browser trust the mirror over the origin. This needs the gate ADR's +pinning/retention answer (who keeps the content available, and for how long) but not its full +gateway-trust or multi-origin-CSP answer, since nothing about how the *default* dashboard is served +changes yet. + +**Stage 2 — content-addressed as an explicit alternative, CSP widened deliberately.** Once ADR-012 +Stage 1 (#34, multi-Control-Plane) has landed, widen `connect-src` to a configured allowlist of +known Control Plane API origins and document a supported way to run the dashboard build from a +content-addressed gateway against any of them. This is the point where the wallet-login +origin-scoping question above must be answered (widen key storage deliberately, or accept +per-origin re-login) — that decision belongs in the gate ADR, not here. + +**Stage 3 — content-addressed as the default, Control-Plane-served becomes the fallback.** Flip the +default entry point once Stage 2 has run in production long enough to trust its gateway/pinning +story, keeping direct Control-Plane serving available per ADR-012 §7's rollback rule. + +## What this document does not do + +- It does not choose a content-addressing technology, a pinning provider, or a CDN — those are the + gate ADR's decisions (ADR-012 §6). +- It does not authorize starting Stage 0.5 or later — Stage 0.5 needs no ADR per the reasoning + above, but Stage 1 onward is gated the same way every other item in ADR-012 §6 is: "No + implementation in those milestones starts before its gate ADR is accepted" (`ROADMAP.md`). +- It does not resolve the wallet-login origin-scoping question raised in Stage 2 — flagged for the + gate ADR to settle, not decided here. + +## Relationship to ADR-012 + +This document elaborates ADR-012 §3's "Dashboard / frontend assets" row and §6's `ADR-021` gate +(originally reserved as `ADR-018` before the renumbering documented in ADR-012's own +"Consequences" section) without being that gate ADR itself. When `ADR-021` is written, it should +supersede this document's staging proposal or explicitly adopt it — this file is not meant to be +maintained in parallel with an eventually-accepted ADR covering the same ground.